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, *location2;
01954 const char *macrocontext, *macroexten;
01955 char pickupmark[256], chan[128];
01956
01957
01958 if (qe->parent->wrapuptime && (time(NULL) - tmp->lastcall < qe->parent->wrapuptime)) {
01959 if (queue_debug)
01960 ast_log(LOG_NOTICE, "Wrapuptime not yet expired for %s\n", tmp->interface);
01961 if (qe->chan->cdr)
01962 ast_cdr_busy(qe->chan->cdr);
01963 tmp->stillgoing = 0;
01964 (*busies)++;
01965 return 0;
01966 }
01967
01968 if (!qe->parent->ringinuse && (tmp->member->status != AST_DEVICE_NOT_INUSE) && (tmp->member->status != AST_DEVICE_UNKNOWN)) {
01969 if (queue_debug)
01970 ast_log(LOG_NOTICE, "%s in use, can't receive call\n", tmp->interface);
01971 if (qe->chan->cdr)
01972 ast_cdr_busy(qe->chan->cdr);
01973 tmp->stillgoing = 0;
01974 return 0;
01975 }
01976
01977 if (tmp->member->paused) {
01978 if (queue_debug)
01979 ast_log(LOG_NOTICE, "%s paused, can't receive call\n", tmp->interface);
01980 if (qe->chan->cdr)
01981 ast_cdr_busy(qe->chan->cdr);
01982 tmp->stillgoing = 0;
01983 return 0;
01984 }
01985 if (use_weight && compare_weight(qe->parent,tmp->member)) {
01986 if (queue_debug)
01987 ast_log(LOG_NOTICE, "Priority queue delaying call to %s:%s\n", qe->parent->name, tmp->interface);
01988 if (qe->chan->cdr)
01989 ast_cdr_busy(qe->chan->cdr);
01990 tmp->stillgoing = 0;
01991 (*busies)++;
01992 return 0;
01993 }
01994
01995 ast_copy_string(tech, tmp->interface, sizeof(tech));
01996 if ((location = strchr(tech, '/')))
01997 *location++ = '\0';
01998 else
01999 location = "";
02000
02001
02002 tmp->chan = ast_request(tech, qe->chan->nativeformats, location, &status);
02003 if (!tmp->chan) {
02004 if (queue_debug)
02005 ast_log(LOG_NOTICE, "Unable to create channel of type '%s' for Queue\n", tech);
02006 if (qe->chan->cdr)
02007 ast_cdr_busy(qe->chan->cdr);
02008 tmp->stillgoing = 0;
02009
02010 update_status(tmp->member->state_interface, ast_device_state(tmp->member->state_interface));
02011
02012 ao2_lock(qe->parent);
02013 qe->parent->rrpos++;
02014 ao2_unlock(qe->parent);
02015
02016 (*busies)++;
02017 return 0;
02018 }
02019
02020
02021 tmp->member->ringcount++;
02022 tmp->chan->appl = "AppQueue";
02023 tmp->chan->data = "(Outgoing Line)";
02024 tmp->chan->whentohangup = 0;
02025 if (tmp->chan->cid.cid_num)
02026 free(tmp->chan->cid.cid_num);
02027 tmp->chan->cid.cid_num = ast_strdup(qe->chan->cid.cid_num);
02028 if (tmp->chan->cid.cid_name)
02029 free(tmp->chan->cid.cid_name);
02030 tmp->chan->cid.cid_name = ast_strdup(qe->chan->cid.cid_name);
02031 if (tmp->chan->cid.cid_ani)
02032 free(tmp->chan->cid.cid_ani);
02033 tmp->chan->cid.cid_ani = ast_strdup(qe->chan->cid.cid_ani);
02034
02035
02036 ast_channel_inherit_variables(qe->chan, tmp->chan);
02037 ast_channel_datastore_inherit(qe->chan, tmp->chan);
02038
02039
02040 tmp->chan->adsicpe = qe->chan->adsicpe;
02041
02042
02043 ast_channel_lock(qe->chan);
02044 macrocontext = pbx_builtin_getvar_helper(qe->chan, "MACRO_CONTEXT");
02045 if (!ast_strlen_zero(macrocontext))
02046 ast_copy_string(tmp->chan->dialcontext, macrocontext, sizeof(tmp->chan->dialcontext));
02047 else
02048 ast_copy_string(tmp->chan->dialcontext, qe->chan->context, sizeof(tmp->chan->dialcontext));
02049 macroexten = pbx_builtin_getvar_helper(qe->chan, "MACRO_EXTEN");
02050 if (!ast_strlen_zero(macroexten))
02051 ast_copy_string(tmp->chan->exten, macroexten, sizeof(tmp->chan->exten));
02052 else
02053 ast_copy_string(tmp->chan->exten, qe->chan->exten, sizeof(tmp->chan->exten));
02054 if (ast_cdr_isset_unanswered()) {
02055
02056
02057 ast_cdr_setdestchan(tmp->chan->cdr, tmp->chan->name);
02058 strcpy(tmp->chan->cdr->clid, qe->chan->cdr->clid);
02059 strcpy(tmp->chan->cdr->channel, qe->chan->cdr->channel);
02060 strcpy(tmp->chan->cdr->src, qe->chan->cdr->src);
02061 strcpy(tmp->chan->cdr->dst, qe->chan->exten);
02062 strcpy(tmp->chan->cdr->dcontext, qe->chan->context);
02063 strcpy(tmp->chan->cdr->lastapp, qe->chan->cdr->lastapp);
02064 strcpy(tmp->chan->cdr->lastdata, qe->chan->cdr->lastdata);
02065 tmp->chan->cdr->amaflags = qe->chan->cdr->amaflags;
02066 strcpy(tmp->chan->cdr->accountcode, qe->chan->cdr->accountcode);
02067 strcpy(tmp->chan->cdr->userfield, qe->chan->cdr->userfield);
02068 }
02069 ast_channel_unlock(qe->chan);
02070
02071
02072 if (option_debug > 2)
02073 ast_log(LOG_DEBUG, "chan %s, tech: %s, part %s\n", tmp->chan->name, tech, location);
02074
02075 ast_copy_string(chan, location, sizeof(chan));
02076 if (!strncasecmp(tech, "dahdi", 5)) {
02077 if((chan[0] > '0') && (chan[0] <= '9')) {
02078 if ((location2 = strchr(chan, 'r')))
02079 *location2++ = '\0';
02080 }
02081 }
02082 snprintf(pickupmark, sizeof(pickupmark), "%s/%s", tech, chan);
02083 pbx_builtin_setvar_helper(tmp->chan, "PICKUPMARK", pickupmark);
02084
02085
02086 if ((res = ast_call(tmp->chan, location, 0))) {
02087
02088 if (option_debug)
02089 ast_log(LOG_DEBUG, "ast call on peer returned %d\n", res);
02090 if (option_verbose > 2)
02091 ast_verbose(VERBOSE_PREFIX_3 "Couldn't call %s\n", tmp->interface);
02092 do_hang(tmp);
02093 (*busies)++;
02094 update_status(tmp->member->state_interface, ast_device_state(tmp->member->state_interface));
02095 return 0;
02096 } else if (qe->parent->eventwhencalled) {
02097 char vars[2048];
02098
02099 manager_event(EVENT_FLAG_AGENT, "AgentCalled",
02100 "AgentCalled: %s\r\n"
02101 "AgentName: %s\r\n"
02102 "ChannelCalling: %s\r\n"
02103 "CallerID: %s\r\n"
02104 "CallerIDName: %s\r\n"
02105 "Context: %s\r\n"
02106 "Extension: %s\r\n"
02107 "Priority: %d\r\n"
02108 "%s",
02109 tmp->interface, tmp->member->membername, qe->chan->name,
02110 tmp->chan->cid.cid_num ? tmp->chan->cid.cid_num : "unknown",
02111 tmp->chan->cid.cid_name ? tmp->chan->cid.cid_name : "unknown",
02112 qe->chan->context, qe->chan->exten, qe->chan->priority,
02113 qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
02114 if (option_verbose > 2)
02115 ast_verbose(VERBOSE_PREFIX_3 "Called %s\n", tmp->interface);
02116 }
02117
02118 update_status(tmp->member->state_interface, ast_device_state(tmp->member->state_interface));
02119 return 1;
02120 }
02121
02122
02123 static struct callattempt *find_best(struct callattempt *outgoing)
02124 {
02125 struct callattempt *best = NULL, *cur;
02126
02127 for (cur = outgoing; cur; cur = cur->q_next) {
02128 if (cur->stillgoing &&
02129 !cur->chan &&
02130 (!best || cur->metric < best->metric)) {
02131 best = cur;
02132 }
02133 }
02134
02135 return best;
02136 }
02137
02138
02139
02140
02141
02142
02143
02144
02145
02146 static int ring_one(struct queue_ent *qe, struct callattempt *outgoing, int *busies)
02147 {
02148 int ret = 0;
02149
02150 while (ret == 0) {
02151 struct callattempt *best = find_best(outgoing);
02152 if (!best) {
02153 if (option_debug)
02154 ast_log(LOG_DEBUG, "Nobody left to try ringing in queue\n");
02155 break;
02156 }
02157 if (qe->parent->strategy == QUEUE_STRATEGY_RINGALL) {
02158 struct callattempt *cur;
02159
02160 for (cur = outgoing; cur; cur = cur->q_next) {
02161 if (cur->stillgoing && !cur->chan && cur->metric <= best->metric) {
02162 if (option_debug)
02163 ast_log(LOG_DEBUG, "(Parallel) Trying '%s' with metric %d\n", cur->interface, cur->metric);
02164 ret |= ring_entry(qe, cur, busies);
02165 }
02166 }
02167 } else {
02168
02169 if (option_debug)
02170 ast_log(LOG_DEBUG, "Trying '%s' with metric %d\n", best->interface, best->metric);
02171 ret = ring_entry(qe, best, busies);
02172 }
02173 }
02174
02175 return ret;
02176 }
02177
02178 static int store_next(struct queue_ent *qe, struct callattempt *outgoing)
02179 {
02180 struct callattempt *best = find_best(outgoing);
02181
02182 if (best) {
02183
02184 if (option_debug)
02185 ast_log(LOG_DEBUG, "Next is '%s' with metric %d\n", best->interface, best->metric);
02186 qe->parent->rrpos = best->metric % 1000;
02187 } else {
02188
02189 if (qe->parent->wrapped) {
02190
02191 qe->parent->rrpos = 0;
02192 } else {
02193
02194 qe->parent->rrpos++;
02195 }
02196 }
02197 qe->parent->wrapped = 0;
02198
02199 return 0;
02200 }
02201
02202 static int say_periodic_announcement(struct queue_ent *qe)
02203 {
02204 int res = 0;
02205 time_t now;
02206
02207
02208 time(&now);
02209
02210
02211 if ((now - qe->last_periodic_announce_time) < qe->parent->periodicannouncefrequency)
02212 return 0;
02213
02214
02215 ast_moh_stop(qe->chan);
02216
02217 if (option_verbose > 2)
02218 ast_verbose(VERBOSE_PREFIX_3 "Playing periodic announcement\n");
02219
02220
02221 if (qe->last_periodic_announce_sound >= MAX_PERIODIC_ANNOUNCEMENTS || !strlen(qe->parent->sound_periodicannounce[qe->last_periodic_announce_sound])) {
02222 qe->last_periodic_announce_sound = 0;
02223 }
02224
02225
02226 res = play_file(qe->chan, qe->parent->sound_periodicannounce[qe->last_periodic_announce_sound]);
02227
02228 if (res > 0 && !valid_exit(qe, res))
02229 res = 0;
02230
02231
02232 if (!res)
02233 ast_moh_start(qe->chan, qe->moh, NULL);
02234
02235
02236 qe->last_periodic_announce_time = now;
02237
02238
02239 qe->last_periodic_announce_sound++;
02240
02241 return res;
02242 }
02243
02244 static void record_abandoned(struct queue_ent *qe)
02245 {
02246 ao2_lock(qe->parent);
02247 manager_event(EVENT_FLAG_AGENT, "QueueCallerAbandon",
02248 "Queue: %s\r\n"
02249 "Uniqueid: %s\r\n"
02250 "Position: %d\r\n"
02251 "OriginalPosition: %d\r\n"
02252 "HoldTime: %d\r\n",
02253 qe->parent->name, qe->chan->uniqueid, qe->pos, qe->opos, (int)(time(NULL) - qe->start));
02254
02255 qe->parent->callsabandoned++;
02256 ao2_unlock(qe->parent);
02257 }
02258
02259
02260 static void rna(int rnatime, struct queue_ent *qe, char *interface, char *membername, int pause)
02261 {
02262 if (option_verbose > 2)
02263 ast_verbose( VERBOSE_PREFIX_3 "Nobody picked up in %d ms\n", rnatime);
02264
02265
02266 if (qe->ring_when_ringing) {
02267 ast_indicate(qe->chan, -1);
02268 ast_moh_start(qe->chan, qe->moh, NULL);
02269 }
02270
02271 ast_queue_log(qe->parent->name, qe->chan->uniqueid, membername, "RINGNOANSWER", "%d", rnatime);
02272 if (qe->parent->autopause && pause) {
02273 if (!set_member_paused(qe->parent->name, interface, 1)) {
02274 if (option_verbose > 2)
02275 ast_verbose( VERBOSE_PREFIX_3 "Auto-Pausing Queue Member %s in queue %s since they failed to answer.\n", interface, qe->parent->name);
02276 } else {
02277 if (option_verbose > 2)
02278 ast_verbose( VERBOSE_PREFIX_3 "Failed to pause Queue Member %s in queue %s!\n", interface, qe->parent->name);
02279 }
02280 }
02281 return;
02282 }
02283
02284 #define AST_MAX_WATCHERS 256
02285
02286
02287
02288
02289
02290
02291
02292
02293
02294
02295 static struct callattempt *wait_for_answer(struct queue_ent *qe, struct callattempt *outgoing, int *to, char *digit, int prebusies, int caller_disconnect, int forwardsallowed)
02296 {
02297 char *queue = qe->parent->name;
02298 struct callattempt *o, *start = NULL, *prev = NULL;
02299 int status;
02300 int numbusies = prebusies;
02301 int numnochan = 0;
02302 int stillgoing = 0;
02303 int orig = *to;
02304 struct ast_frame *f;
02305 struct callattempt *peer = NULL;
02306 struct ast_channel *winner;
02307 struct ast_channel *in = qe->chan;
02308 char on[80] = "";
02309 char membername[80] = "";
02310 long starttime = 0;
02311 long endtime = 0;
02312
02313 starttime = (long) time(NULL);
02314
02315 while (*to && !peer) {
02316 int numlines, retry, pos = 1;
02317 struct ast_channel *watchers[AST_MAX_WATCHERS];
02318 watchers[0] = in;
02319 start = NULL;
02320
02321 for (retry = 0; retry < 2; retry++) {
02322 numlines = 0;
02323 for (o = outgoing; o; o = o->q_next) {
02324 if (o->stillgoing) {
02325 stillgoing = 1;
02326 if (o->chan) {
02327 watchers[pos++] = o->chan;
02328 if (!start)
02329 start = o;
02330 else
02331 prev->call_next = o;
02332 prev = o;
02333 }
02334 }
02335 numlines++;
02336 }
02337 if (pos > 1 || !stillgoing ||
02338 (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) )
02339 break;
02340
02341
02342 ring_one(qe, outgoing, &numbusies);
02343
02344 }
02345 if (pos == 1 ) {
02346 if (numlines == (numbusies + numnochan)) {
02347 ast_log(LOG_DEBUG, "Everyone is busy at this time\n");
02348 } else {
02349 ast_log(LOG_NOTICE, "No one is answering queue '%s' (%d/%d/%d)\n", queue, numlines, numbusies, numnochan);
02350 }
02351 *to = 0;
02352 return NULL;
02353 }
02354
02355
02356 winner = ast_waitfor_n(watchers, pos, to);
02357
02358
02359 for (o = start; o; o = o->call_next) {
02360 if (o->stillgoing && (o->chan) && (o->chan->_state == AST_STATE_UP)) {
02361 if (!peer) {
02362 if (option_verbose > 2)
02363 ast_verbose( VERBOSE_PREFIX_3 "%s answered %s\n", o->chan->name, in->name);
02364 peer = o;
02365 }
02366 } else if (o->chan && (o->chan == winner)) {
02367
02368 ast_copy_string(on, o->member->interface, sizeof(on));
02369 ast_copy_string(membername, o->member->membername, sizeof(membername));
02370
02371 if (!ast_strlen_zero(o->chan->call_forward) && !forwardsallowed) {
02372 if (option_verbose > 2)
02373 ast_verbose(VERBOSE_PREFIX_3 "Forwarding %s to '%s' prevented.\n", in->name, o->chan->call_forward);
02374 numnochan++;
02375 do_hang(o);
02376 winner = NULL;
02377 continue;
02378 } else if (!ast_strlen_zero(o->chan->call_forward)) {
02379 char tmpchan[256];
02380 char *stuff;
02381 char *tech;
02382
02383 ast_copy_string(tmpchan, o->chan->call_forward, sizeof(tmpchan));
02384 if ((stuff = strchr(tmpchan, '/'))) {
02385 *stuff++ = '\0';
02386 tech = tmpchan;
02387 } else {
02388 snprintf(tmpchan, sizeof(tmpchan), "%s@%s", o->chan->call_forward, o->chan->context);
02389 stuff = tmpchan;
02390 tech = "Local";
02391 }
02392
02393 if (option_verbose > 2)
02394 ast_verbose(VERBOSE_PREFIX_3 "Now forwarding %s to '%s/%s' (thanks to %s)\n", in->name, tech, stuff, o->chan->name);
02395
02396 o->chan = ast_request(tech, in->nativeformats, stuff, &status);
02397 if (!o->chan) {
02398 ast_log(LOG_NOTICE,
02399 "Forwarding failed to create channel to dial '%s/%s'\n",
02400 tech, stuff);
02401 o->stillgoing = 0;
02402 numnochan++;
02403 } else {
02404 ast_channel_inherit_variables(in, o->chan);
02405 ast_channel_datastore_inherit(in, o->chan);
02406 if (o->chan->cid.cid_num)
02407 free(o->chan->cid.cid_num);
02408 o->chan->cid.cid_num = ast_strdup(in->cid.cid_num);
02409
02410 if (o->chan->cid.cid_name)
02411 free(o->chan->cid.cid_name);
02412 o->chan->cid.cid_name = ast_strdup(in->cid.cid_name);
02413
02414 ast_string_field_set(o->chan, accountcode, in->accountcode);
02415 o->chan->cdrflags = in->cdrflags;
02416
02417 if (in->cid.cid_ani) {
02418 if (o->chan->cid.cid_ani)
02419 free(o->chan->cid.cid_ani);
02420 o->chan->cid.cid_ani = ast_strdup(in->cid.cid_ani);
02421 }
02422 if (o->chan->cid.cid_rdnis)
02423 free(o->chan->cid.cid_rdnis);
02424 o->chan->cid.cid_rdnis = ast_strdup(S_OR(in->macroexten, in->exten));
02425 if (ast_call(o->chan, stuff, 0)) {
02426 ast_log(LOG_NOTICE, "Forwarding failed to dial '%s/%s'\n",
02427 tech, stuff);
02428 do_hang(o);
02429 numnochan++;
02430 }
02431 }
02432
02433 ast_hangup(winner);
02434 continue;
02435 }
02436 f = ast_read(winner);
02437 if (f) {
02438 if (f->frametype == AST_FRAME_CONTROL) {
02439 switch (f->subclass) {
02440 case AST_CONTROL_ANSWER:
02441
02442 if (!peer) {
02443 if (option_verbose > 2)
02444 ast_verbose( VERBOSE_PREFIX_3 "%s answered %s\n", o->chan->name, in->name);
02445 peer = o;
02446 }
02447 break;
02448 case AST_CONTROL_BUSY:
02449 if (option_verbose > 2)
02450 ast_verbose( VERBOSE_PREFIX_3 "%s is busy\n", o->chan->name);
02451 if (in->cdr)
02452 ast_cdr_busy(in->cdr);
02453 do_hang(o);
02454 endtime = (long)time(NULL);
02455 endtime -= starttime;
02456 rna(endtime * 1000, qe, on, membername, 0);
02457 if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) {
02458 if (qe->parent->timeoutrestart)
02459 *to = orig;
02460
02461 if (*to > 500) {
02462 ring_one(qe, outgoing, &numbusies);
02463 starttime = (long) time(NULL);
02464 }
02465 }
02466 numbusies++;
02467 break;
02468 case AST_CONTROL_CONGESTION:
02469 if (option_verbose > 2)
02470 ast_verbose( VERBOSE_PREFIX_3 "%s is circuit-busy\n", o->chan->name);
02471 if (in->cdr)
02472 ast_cdr_busy(in->cdr);
02473 endtime = (long)time(NULL);
02474 endtime -= starttime;
02475 rna(endtime * 1000, qe, on, membername, 0);
02476 do_hang(o);
02477 if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) {
02478 if (qe->parent->timeoutrestart)
02479 *to = orig;
02480 if (*to > 500) {
02481 ring_one(qe, outgoing, &numbusies);
02482 starttime = (long) time(NULL);
02483 }
02484 }
02485 numbusies++;
02486 break;
02487 case AST_CONTROL_RINGING:
02488 if (option_verbose > 2)
02489 ast_verbose( VERBOSE_PREFIX_3 "%s is ringing\n", o->chan->name);
02490
02491
02492 if (qe->ring_when_ringing) {
02493 ast_moh_stop(qe->chan);
02494 ast_indicate(qe->chan, AST_CONTROL_RINGING);
02495 }
02496 break;
02497 case AST_CONTROL_OFFHOOK:
02498
02499 break;
02500 default:
02501 ast_log(LOG_DEBUG, "Dunno what to do with control type %d\n", f->subclass);
02502 }
02503 }
02504 ast_frfree(f);
02505 } else {
02506 endtime = (long) time(NULL) - starttime;
02507 rna(endtime * 1000, qe, on, membername, 1);
02508 do_hang(o);
02509 if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) {
02510 if (qe->parent->timeoutrestart)
02511 *to = orig;
02512 if (*to > 500) {
02513 ring_one(qe, outgoing, &numbusies);
02514 starttime = (long) time(NULL);
02515 }
02516 }
02517 }
02518 }
02519 }
02520
02521
02522 if (winner == in) {
02523 f = ast_read(in);
02524 if (!f || ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_HANGUP))) {
02525
02526 *to = -1;
02527 if (f)
02528 ast_frfree(f);
02529 return NULL;
02530 }
02531
02532 if ((f->frametype == AST_FRAME_DTMF) && valid_exit(qe, f->subclass)) {
02533 if (option_verbose > 3)
02534 ast_verbose(VERBOSE_PREFIX_3 "User pressed digit: %c\n", f->subclass);
02535 *to = 0;
02536 *digit = f->subclass;
02537 ast_frfree(f);
02538 return NULL;
02539 }
02540
02541 if ((f->frametype == AST_FRAME_DTMF) && caller_disconnect && (f->subclass == '*')) {
02542 if (option_verbose > 3)
02543 ast_verbose(VERBOSE_PREFIX_3 "User hit %c to disconnect call.\n", f->subclass);
02544 *to = 0;
02545 ast_frfree(f);
02546 return NULL;
02547 }
02548 ast_frfree(f);
02549 }
02550 if (!*to) {
02551 for (o = start; o; o = o->call_next)
02552 rna(orig, qe, o->interface, o->member->membername, 1);
02553 }
02554 }
02555
02556 return peer;
02557 }
02558
02559
02560
02561
02562
02563
02564
02565
02566
02567
02568
02569 static int is_our_turn(struct queue_ent *qe)
02570 {
02571 struct queue_ent *ch;
02572 int res;
02573 int avl;
02574 int idx = 0;
02575
02576 ao2_lock(qe->parent);
02577
02578 avl = num_available_members(qe->parent);
02579
02580 ch = qe->parent->head;
02581
02582 if (option_debug) {
02583 ast_log(LOG_DEBUG, "There %s %d available %s.\n", avl != 1 ? "are" : "is", avl, avl != 1 ? "members" : "member");
02584 }
02585
02586 while ((idx < avl) && (ch) && (ch != qe)) {
02587 if (!ch->pending)
02588 idx++;
02589 ch = ch->next;
02590 }
02591
02592 ao2_unlock(qe->parent);
02593
02594
02595
02596
02597 if (ch && idx < avl && (qe->parent->autofill || qe->pos == 1)) {
02598 if (option_debug)
02599 ast_log(LOG_DEBUG, "It's our turn (%s).\n", qe->chan->name);
02600 res = 1;
02601 } else {
02602 if (option_debug)
02603 ast_log(LOG_DEBUG, "It's not our turn (%s).\n", qe->chan->name);
02604 res = 0;
02605 }
02606
02607 return res;
02608 }
02609
02610
02611
02612
02613
02614
02615
02616
02617
02618
02619 static int wait_our_turn(struct queue_ent *qe, int ringing, enum queue_result *reason)
02620 {
02621 int res = 0;
02622
02623
02624 for (;;) {
02625 enum queue_member_status stat;
02626
02627 if (is_our_turn(qe))
02628 break;
02629
02630
02631 if (qe->expire && (time(NULL) >= qe->expire)) {
02632 *reason = QUEUE_TIMEOUT;
02633 break;
02634 }
02635
02636 stat = get_member_status(qe->parent, qe->max_penalty);
02637
02638
02639 if (qe->parent->leavewhenempty && (stat == QUEUE_NO_MEMBERS)) {
02640 *reason = QUEUE_LEAVEEMPTY;
02641 ast_queue_log(qe->parent->name, qe->chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe->pos, qe->opos, (long)time(NULL) - qe->start);
02642 leave_queue(qe);
02643 break;
02644 }
02645
02646
02647 if ((qe->parent->leavewhenempty == QUEUE_EMPTY_STRICT) && (stat == QUEUE_NO_REACHABLE_MEMBERS)) {
02648 *reason = QUEUE_LEAVEUNAVAIL;
02649 ast_queue_log(qe->parent->name, qe->chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe->pos, qe->opos, (long)time(NULL) - qe->start);
02650 leave_queue(qe);
02651 break;
02652 }
02653
02654
02655 if (qe->parent->announcefrequency && !ringing &&
02656 (res = say_position(qe)))
02657 break;
02658
02659
02660 if (qe->expire && (time(NULL) >= qe->expire)) {
02661 *reason = QUEUE_TIMEOUT;
02662 break;
02663 }
02664
02665
02666 if (qe->parent->periodicannouncefrequency && !ringing &&
02667 (res = say_periodic_announcement(qe)))
02668 break;
02669
02670
02671 if (qe->expire && (time(NULL) >= qe->expire)) {
02672 *reason = QUEUE_TIMEOUT;
02673 break;
02674 }
02675
02676
02677 if ((res = ast_waitfordigit(qe->chan, RECHECK * 1000))) {
02678 if (res > 0 && !valid_exit(qe, res))
02679 res = 0;
02680 else
02681 break;
02682 }
02683
02684
02685 if (qe->expire && (time(NULL) >= qe->expire)) {
02686 *reason = QUEUE_TIMEOUT;
02687 break;
02688 }
02689 }
02690
02691 return res;
02692 }
02693
02694 static int update_queue(struct call_queue *q, struct member *member, int callcompletedinsl)
02695 {
02696 ao2_lock(q);
02697 time(&member->lastcall);
02698 member->calls++;
02699 q->callscompleted++;
02700 if (callcompletedinsl)
02701 q->callscompletedinsl++;
02702 ao2_unlock(q);
02703 return 0;
02704 }
02705
02706
02707
02708
02709
02710
02711
02712 static int calc_metric(struct call_queue *q, struct member *mem, int pos, struct queue_ent *qe, struct callattempt *tmp)
02713 {
02714 if (qe->max_penalty && (mem->penalty > qe->max_penalty))
02715 return -1;
02716
02717 switch (q->strategy) {
02718 case QUEUE_STRATEGY_RINGALL:
02719
02720 tmp->metric = mem->penalty * 1000000;
02721 break;
02722 case QUEUE_STRATEGY_ROUNDROBIN:
02723 if (!pos) {
02724 if (!q->wrapped) {
02725
02726 q->rrpos = 0;
02727 } else {
02728
02729 q->rrpos++;
02730 }
02731 q->wrapped = 0;
02732 }
02733
02734 case QUEUE_STRATEGY_RRORDERED:
02735 case QUEUE_STRATEGY_RRMEMORY:
02736 if (pos < q->rrpos) {
02737 tmp->metric = 1000 + pos;
02738 } else {
02739 if (pos > q->rrpos)
02740
02741 q->wrapped = 1;
02742 tmp->metric = pos;
02743 }
02744 tmp->metric += mem->penalty * 1000000;
02745 break;
02746 case QUEUE_STRATEGY_RANDOM:
02747 tmp->metric = ast_random() % 1000;
02748 tmp->metric += mem->penalty * 1000000;
02749 break;
02750 case QUEUE_STRATEGY_FEWESTCALLS:
02751 tmp->metric = mem->calls;
02752 tmp->metric += mem->penalty * 1000000;
02753 break;
02754 case QUEUE_STRATEGY_LEASTRECENT:
02755 if (!mem->lastcall)
02756 tmp->metric = 0;
02757 else
02758 tmp->metric = 1000000 - (time(NULL) - mem->lastcall);
02759 tmp->metric += mem->penalty * 1000000;
02760 break;
02761 default:
02762 ast_log(LOG_WARNING, "Can't calculate metric for unknown strategy %d\n", q->strategy);
02763 break;
02764 }
02765 if (q->ringlimit && (mem->ringcount >= q->ringlimit)) {
02766 tmp->metric += (mem->ringcount / q->ringlimit) * 10000000;
02767 }
02768 if (option_debug)
02769 ast_log(LOG_DEBUG, "New metric %d for member %s with %d rings (limit %d)\n",
02770 tmp->metric, mem->interface, mem->ringcount, q->ringlimit);
02771 return 0;
02772 }
02773
02774 struct queue_transfer_ds {
02775 struct queue_ent *qe;
02776 struct member *member;
02777 time_t starttime;
02778 int callcompletedinsl;
02779 };
02780
02781 static void queue_transfer_destroy(void *data)
02782 {
02783 struct queue_transfer_ds *qtds = data;
02784 ast_free(qtds);
02785 }
02786
02787
02788
02789 static const struct ast_datastore_info queue_transfer_info = {
02790 .type = "queue_transfer",
02791 .chan_fixup = queue_transfer_fixup,
02792 .destroy = queue_transfer_destroy,
02793 };
02794
02795
02796
02797
02798
02799
02800
02801
02802
02803
02804 static void queue_transfer_fixup(void *data, struct ast_channel *old_chan, struct ast_channel *new_chan)
02805 {
02806 struct queue_transfer_ds *qtds = data;
02807 struct queue_ent *qe = qtds->qe;
02808 struct member *member = qtds->member;
02809 time_t callstart = qtds->starttime;
02810 int callcompletedinsl = qtds->callcompletedinsl;
02811 struct ast_datastore *datastore;
02812
02813 ast_queue_log(qe->parent->name, qe->chan->uniqueid, member->membername, "TRANSFER", "%s|%s|%ld|%ld",
02814 new_chan->exten, new_chan->context, (long) (callstart - qe->start),
02815 (long) (time(NULL) - callstart));
02816
02817 update_queue(qe->parent, member, callcompletedinsl);
02818
02819
02820 if ((datastore = ast_channel_datastore_find(old_chan, &queue_transfer_info, NULL))) {
02821 ast_channel_datastore_remove(old_chan, datastore);
02822 } else {
02823 ast_log(LOG_WARNING, "Can't find the queue_transfer datastore.\n");
02824 }
02825 }
02826
02827
02828
02829
02830
02831
02832
02833
02834
02835 static int attended_transfer_occurred(struct ast_channel *chan)
02836 {
02837 return ast_channel_datastore_find(chan, &queue_transfer_info, NULL) ? 0 : 1;
02838 }
02839
02840
02841
02842 static struct ast_datastore *setup_transfer_datastore(struct queue_ent *qe, struct member *member, time_t starttime, int callcompletedinsl)
02843 {
02844 struct ast_datastore *ds;
02845 struct queue_transfer_ds *qtds = ast_calloc(1, sizeof(*qtds));
02846
02847 if (!qtds) {
02848 ast_log(LOG_WARNING, "Memory allocation error!\n");
02849 return NULL;
02850 }
02851
02852 ast_channel_lock(qe->chan);
02853 if (!(ds = ast_channel_datastore_alloc(&queue_transfer_info, NULL))) {
02854 ast_channel_unlock(qe->chan);
02855 ast_log(LOG_WARNING, "Unable to create transfer datastore. queue_log will not show attended transfer\n");
02856 return NULL;
02857 }
02858
02859 qtds->qe = qe;
02860
02861 qtds->member = member;
02862 qtds->starttime = starttime;
02863 qtds->callcompletedinsl = callcompletedinsl;
02864 ds->data = qtds;
02865 ast_channel_datastore_add(qe->chan, ds);
02866 ast_channel_unlock(qe->chan);
02867 return ds;
02868 }
02869
02870
02871
02872
02873
02874
02875
02876
02877
02878
02879
02880
02881
02882
02883
02884
02885
02886
02887
02888
02889
02890
02891
02892
02893
02894
02895 static int try_calling(struct queue_ent *qe, const char *options, char *announceoverride, const char *url, int *tries, int *noption, const char *agi)
02896 {
02897 struct member *cur;
02898 struct callattempt *outgoing = NULL;
02899 int to;
02900 char oldexten[AST_MAX_EXTENSION]="";
02901 char oldcontext[AST_MAX_CONTEXT]="";
02902 char queuename[256]="";
02903 struct ast_channel *peer;
02904 struct ast_channel *which;
02905 struct callattempt *lpeer;
02906 struct member *member;
02907 struct ast_app *app;
02908 int res = 0, bridge = 0;
02909 int numbusies = 0;
02910 int x=0;
02911 char *announce = NULL;
02912 char digit = 0;
02913 time_t callstart;
02914 time_t now = time(NULL);
02915 struct ast_bridge_config bridge_config;
02916 char nondataquality = 1;
02917 char *agiexec = NULL;
02918 int ret = 0;
02919 const char *monitorfilename;
02920 const char *monitor_exec;
02921 const char *monitor_options;
02922 char tmpid[256], tmpid2[256];
02923 char meid[1024], meid2[1024];
02924 char mixmonargs[1512];
02925 struct ast_app *mixmonapp = NULL;
02926 char *p;
02927 char vars[2048];
02928 int forwardsallowed = 1;
02929 int callcompletedinsl;
02930 struct ao2_iterator memi;
02931 struct ast_datastore *datastore, *transfer_ds;
02932 const int need_weight = use_weight;
02933
02934 ast_channel_lock(qe->chan);
02935 datastore = ast_channel_datastore_find(qe->chan, &dialed_interface_info, NULL);
02936 ast_channel_unlock(qe->chan);
02937
02938 memset(&bridge_config, 0, sizeof(bridge_config));
02939 time(&now);
02940
02941
02942
02943
02944
02945 if (qe->expire && now >= qe->expire) {
02946 res = 0;
02947 goto out;
02948 }
02949
02950 for (; options && *options; options++)
02951 switch (*options) {
02952 case 't':
02953 ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_REDIRECT);
02954 break;
02955 case 'T':
02956 ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_REDIRECT);
02957 break;
02958 case 'w':
02959 ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_AUTOMON);
02960 break;
02961 case 'W':
02962 ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_AUTOMON);
02963 break;
02964 case 'd':
02965 nondataquality = 0;
02966 break;
02967 case 'h':
02968 ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_DISCONNECT);
02969 break;
02970 case 'H':
02971 ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_DISCONNECT);
02972 break;
02973 case 'n':
02974 if (qe->parent->strategy == QUEUE_STRATEGY_ROUNDROBIN || qe->parent->strategy == QUEUE_STRATEGY_RRMEMORY || qe->parent->strategy == QUEUE_STRATEGY_RRORDERED)
02975 (*tries)++;
02976 else
02977 *tries = qe->parent->membercount;
02978 *noption = 1;
02979 break;
02980 case 'i':
02981 forwardsallowed = 0;
02982 break;
02983 }
02984
02985
02986
02987 if (need_weight)
02988 AST_LIST_LOCK(&queues);
02989 ao2_lock(qe->parent);
02990 if (option_debug)
02991 ast_log(LOG_DEBUG, "%s is trying to call a queue member.\n",
02992 qe->chan->name);
02993 ast_copy_string(queuename, qe->parent->name, sizeof(queuename));
02994 if (!ast_strlen_zero(qe->announce))
02995 announce = qe->announce;
02996 if (!ast_strlen_zero(announceoverride))
02997 announce = announceoverride;
02998
02999 memi = ao2_iterator_init(qe->parent->members, 0);
03000 while ((cur = ao2_iterator_next(&memi))) {
03001 struct callattempt *tmp = ast_calloc(1, sizeof(*tmp));
03002 struct ast_dialed_interface *di;
03003 AST_LIST_HEAD(, ast_dialed_interface) *dialed_interfaces;
03004 if (!tmp) {
03005 ao2_iterator_destroy(&memi);
03006 ao2_ref(cur, -1);
03007 ao2_unlock(qe->parent);
03008 if (need_weight)
03009 AST_LIST_UNLOCK(&queues);
03010 goto out;
03011 }
03012 if (!datastore) {
03013 if (!(datastore = ast_channel_datastore_alloc(&dialed_interface_info, NULL))) {
03014 ao2_iterator_destroy(&memi);
03015 ao2_ref(cur, -1);
03016 ao2_unlock(qe->parent);
03017 if (need_weight)
03018 AST_LIST_UNLOCK(&queues);
03019 free(tmp);
03020 goto out;
03021 }
03022 datastore->inheritance = DATASTORE_INHERIT_FOREVER;
03023 if (!(dialed_interfaces = ast_calloc(1, sizeof(*dialed_interfaces)))) {
03024 ao2_iterator_destroy(&memi);
03025 ao2_ref(cur, -1);
03026 ao2_unlock(qe->parent);
03027 if (need_weight)
03028 AST_LIST_UNLOCK(&queues);
03029 free(tmp);
03030 goto out;
03031 }
03032 datastore->data = dialed_interfaces;
03033 AST_LIST_HEAD_INIT(dialed_interfaces);
03034
03035 ast_channel_lock(qe->chan);
03036 ast_channel_datastore_add(qe->chan, datastore);
03037 ast_channel_unlock(qe->chan);
03038 } else
03039 dialed_interfaces = datastore->data;
03040
03041 AST_LIST_LOCK(dialed_interfaces);
03042 AST_LIST_TRAVERSE(dialed_interfaces, di, list) {
03043 if (!strcasecmp(cur->interface, di->interface)) {
03044 ast_log(LOG_DEBUG, "Skipping dialing interface '%s' since it has already been dialed\n",
03045 di->interface);
03046 break;
03047 }
03048 }
03049 AST_LIST_UNLOCK(dialed_interfaces);
03050
03051 if (di) {
03052 free(tmp);
03053 continue;
03054 }
03055
03056
03057
03058
03059
03060 if (strncasecmp(cur->interface, "Local/", 6)) {
03061 if (!(di = ast_calloc(1, sizeof(*di) + strlen(cur->interface)))) {
03062 ao2_iterator_destroy(&memi);
03063 ao2_ref(cur, -1);
03064 ao2_unlock(qe->parent);
03065 if (need_weight)
03066 AST_LIST_UNLOCK(&queues);
03067 free(tmp);
03068 goto out;
03069 }
03070 strcpy(di->interface, cur->interface);
03071
03072 AST_LIST_LOCK(dialed_interfaces);
03073 AST_LIST_INSERT_TAIL(dialed_interfaces, di, list);
03074 AST_LIST_UNLOCK(dialed_interfaces);
03075 }
03076
03077 tmp->stillgoing = -1;
03078 tmp->member = cur;
03079 tmp->oldstatus = cur->status;
03080 tmp->lastcall = cur->lastcall;
03081 ast_copy_string(tmp->interface, cur->interface, sizeof(tmp->interface));
03082 if (qe->tries == 0 && (cur->ringcount >= qe->parent->ringlimit)) {
03083 cur->ringcount = 0;
03084 }
03085
03086
03087 if (!calc_metric(qe->parent, cur, x++, qe, tmp)) {
03088
03089
03090
03091 tmp->q_next = outgoing;
03092 outgoing = tmp;
03093
03094 if (outgoing->chan && (outgoing->chan->_state == AST_STATE_UP))
03095 break;
03096 } else {
03097 ao2_ref(cur, -1);
03098 free(tmp);
03099 }
03100 }
03101 ao2_iterator_destroy(&memi);
03102 if (qe->expire && (!qe->parent->timeout || (qe->expire - now) <= qe->parent->timeout))
03103 to = (qe->expire - now) * 1000;
03104 else
03105 to = (qe->parent->timeout) ? qe->parent->timeout * 1000 : -1;
03106 ++qe->pending;
03107 ++qe->tries;
03108 if (option_debug)
03109 ast_log(LOG_DEBUG, "%s is trying to ring one member from %s. This is try number %d\n",
03110 qe->chan->name, queuename, qe->tries);
03111 ao2_unlock(qe->parent);
03112 ring_one(qe, outgoing, &numbusies);
03113 if (need_weight)
03114 AST_LIST_UNLOCK(&queues);
03115 lpeer = wait_for_answer(qe, outgoing, &to, &digit, numbusies, ast_test_flag(&(bridge_config.features_caller), AST_FEATURE_DISCONNECT), forwardsallowed);
03116
03117
03118
03119
03120
03121
03122 ast_channel_lock(qe->chan);
03123 if (datastore && !ast_channel_datastore_remove(qe->chan, datastore)) {
03124 ast_channel_datastore_free(datastore);
03125 }
03126 ast_channel_unlock(qe->chan);
03127 ao2_lock(qe->parent);
03128 if (qe->parent->strategy == QUEUE_STRATEGY_RRMEMORY || qe->parent->strategy == QUEUE_STRATEGY_RRORDERED) {
03129 store_next(qe, outgoing);
03130 }
03131 ao2_unlock(qe->parent);
03132 peer = lpeer ? lpeer->chan : NULL;
03133 if (!peer) {
03134 qe->pending = 0;
03135 if (to) {
03136
03137 res = -1;
03138 } else {
03139
03140 res = digit;
03141 }
03142 if (res == -1) {
03143
03144 ast_set_flag(qe->chan->cdr, AST_CDR_FLAG_DONT_TOUCH);
03145 ast_cdr_noanswer(qe->chan->cdr);
03146 if (queue_debug)
03147 ast_log(LOG_NOTICE, "%s: Nobody answered.\n", qe->chan->name);
03148 }
03149 if (qe->parent->eventwhencalled) {
03150 manager_event(EVENT_FLAG_AGENT, "AgentTimeout",
03151 "Queue: %s\r\n"
03152 "ChannelCalling: %s\r\n"
03153 "Uniqueid: %s\r\n"
03154 "Tries: %d\r\n"
03155 "Holdtime: %ld\r\n",
03156 queuename, qe->chan->name, qe->chan->uniqueid, qe->tries,
03157 (long)time(NULL) - qe->start);
03158 }
03159 if (ast_cdr_isset_unanswered()) {
03160
03161
03162 struct callattempt *o;
03163 for (o = outgoing; o; o = o->q_next) {
03164 if (!o->chan) {
03165 continue;
03166 }
03167 if (strcmp(o->chan->cdr->dstchannel, qe->chan->cdr->dstchannel) == 0) {
03168 ast_set_flag(o->chan->cdr, AST_CDR_FLAG_POST_DISABLED);
03169 break;
03170 }
03171 }
03172 }
03173 } else {
03174
03175
03176
03177 if (!strcmp(qe->chan->tech->type, "Zap"))
03178 ast_channel_setoption(qe->chan, AST_OPTION_TONE_VERIFY, &nondataquality, sizeof(nondataquality), 0);
03179 if (!strcmp(peer->tech->type, "Zap"))
03180 ast_channel_setoption(peer, AST_OPTION_TONE_VERIFY, &nondataquality, sizeof(nondataquality), 0);
03181
03182 time(&now);
03183 recalc_holdtime(qe, (now - qe->start));
03184 ao2_lock(qe->parent);
03185 callcompletedinsl = ((now - qe->start) <= qe->parent->servicelevel);
03186 ao2_unlock(qe->parent);
03187 member = lpeer->member;
03188
03189 ao2_ref(member, 1);
03190 hangupcalls(outgoing, peer);
03191 outgoing = NULL;
03192 if (announce || qe->parent->reportholdtime || qe->parent->memberdelay) {
03193 int res2;
03194
03195 res2 = ast_autoservice_start(qe->chan);
03196 if (!res2) {
03197 if (qe->parent->memberdelay) {
03198 ast_log(LOG_NOTICE, "Delaying member connect for %d seconds\n", qe->parent->memberdelay);
03199 res2 |= ast_safe_sleep(peer, qe->parent->memberdelay * 1000);
03200 }
03201 if (!res2 && announce) {
03202 play_file(peer, announce);
03203 }
03204 if (!res2 && qe->parent->reportholdtime) {
03205 if (!play_file(peer, qe->parent->sound_reporthold)) {
03206 int holdtime;
03207
03208 time(&now);
03209 holdtime = abs((now - qe->start) / 60);
03210 if (holdtime < 2) {
03211 play_file(peer, qe->parent->sound_lessthan);
03212 ast_say_number(peer, 2, AST_DIGIT_ANY, peer->language, NULL);
03213 } else
03214 ast_say_number(peer, holdtime, AST_DIGIT_ANY, peer->language, NULL);
03215 play_file(peer, qe->parent->sound_minutes);
03216 }
03217 }
03218 }
03219 res2 |= ast_autoservice_stop(qe->chan);
03220 if (peer->_softhangup) {
03221
03222 ast_log(LOG_WARNING, "Agent on %s hungup on the customer.\n", peer->name);
03223 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "AGENTDUMP", "%s", "");
03224 if (qe->parent->eventwhencalled)
03225 manager_event(EVENT_FLAG_AGENT, "AgentDump",
03226 "Queue: %s\r\n"
03227 "Uniqueid: %s\r\n"
03228 "Channel: %s\r\n"
03229 "Member: %s\r\n"
03230 "MemberName: %s\r\n"
03231 "%s",
03232 queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername,
03233 qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
03234 ast_hangup(peer);
03235 ao2_ref(member, -1);
03236 goto out;
03237 } else if (res2) {
03238
03239 ast_log(LOG_NOTICE, "Caller was about to talk to agent on %s but the caller hungup.\n", peer->name);
03240 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "ABANDON", "%d|%d|%ld", qe->pos, qe->opos, (long)time(NULL) - qe->start);
03241 record_abandoned(qe);
03242 ast_hangup(peer);
03243 ao2_ref(member, -1);
03244 return -1;
03245 }
03246 }
03247
03248 ast_moh_stop(qe->chan);
03249
03250 if (qe->chan->cdr)
03251 ast_cdr_setdestchan(qe->chan->cdr, peer->name);
03252
03253 res = ast_channel_make_compatible(qe->chan, peer);
03254 if (res < 0) {
03255 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "SYSCOMPAT", "%s", "");
03256 ast_log(LOG_WARNING, "Had to drop call because I couldn't make %s compatible with %s\n", qe->chan->name, peer->name);
03257 record_abandoned(qe);
03258 ast_cdr_failed(qe->chan->cdr);
03259 ast_hangup(peer);
03260 ao2_ref(member, -1);
03261 return -1;
03262 }
03263
03264 if (qe->parent->setinterfacevar)
03265 pbx_builtin_setvar_helper(qe->chan, "MEMBERINTERFACE", member->interface);
03266
03267
03268 if (qe->parent->monfmt && *qe->parent->monfmt) {
03269 if (!qe->parent->montype) {
03270 if (option_debug)
03271 ast_log(LOG_DEBUG, "Starting Monitor as requested.\n");
03272 monitorfilename = pbx_builtin_getvar_helper(qe->chan, "MONITOR_FILENAME");
03273 if (pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC") || pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC_ARGS"))
03274 which = qe->chan;
03275 else
03276 which = peer;
03277 if (monitorfilename)
03278 ast_monitor_start(which, qe->parent->monfmt, monitorfilename, 1 );
03279 else if (qe->chan->cdr)
03280 ast_monitor_start(which, qe->parent->monfmt, qe->chan->cdr->uniqueid, 1 );
03281 else {
03282
03283 snprintf(tmpid, sizeof(tmpid), "chan-%lx", ast_random());
03284 ast_monitor_start(which, qe->parent->monfmt, tmpid, 1 );
03285 }
03286 if (qe->parent->monjoin)
03287 ast_monitor_setjoinfiles(which, 1);
03288 } else {
03289 if (option_debug)
03290 ast_log(LOG_DEBUG, "Starting MixMonitor as requested.\n");
03291 monitorfilename = pbx_builtin_getvar_helper(qe->chan, "MONITOR_FILENAME");
03292 if (!monitorfilename) {
03293 if (qe->chan->cdr)
03294 ast_copy_string(tmpid, qe->chan->cdr->uniqueid, sizeof(tmpid)-1);
03295 else
03296 snprintf(tmpid, sizeof(tmpid), "chan-%lx", ast_random());
03297 } else {
03298 ast_copy_string(tmpid2, monitorfilename, sizeof(tmpid2)-1);
03299 for (p = tmpid2; *p ; p++) {
03300 if (*p == '^' && *(p+1) == '{') {
03301 *p = '$';
03302 }
03303 }
03304
03305 memset(tmpid, 0, sizeof(tmpid));
03306 pbx_substitute_variables_helper(qe->chan, tmpid2, tmpid, sizeof(tmpid) - 1);
03307 }
03308
03309 monitor_exec = pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC");
03310 monitor_options = pbx_builtin_getvar_helper(qe->chan, "MONITOR_OPTIONS");
03311
03312 if (monitor_exec) {
03313 ast_copy_string(meid2, monitor_exec, sizeof(meid2)-1);
03314 for (p = meid2; *p ; p++) {
03315 if (*p == '^' && *(p+1) == '{') {
03316 *p = '$';
03317 }
03318 }
03319
03320 memset(meid, 0, sizeof(meid));
03321 pbx_substitute_variables_helper(qe->chan, meid2, meid, sizeof(meid) - 1);
03322 }
03323
03324 snprintf(tmpid2, sizeof(tmpid2)-1, "%s.%s", tmpid, qe->parent->monfmt);
03325
03326 mixmonapp = pbx_findapp("MixMonitor");
03327
03328 if (strchr(tmpid2, '|')) {
03329 ast_log(LOG_WARNING, "monitor-format (in queues.conf) and MONITOR_FILENAME cannot contain a '|'! Not recording.\n");
03330 mixmonapp = NULL;
03331 }
03332
03333 if (!monitor_options)
03334 monitor_options = "";
03335
03336 if (strchr(monitor_options, '|')) {
03337 ast_log(LOG_WARNING, "MONITOR_OPTIONS cannot contain a '|'! Not recording.\n");
03338 mixmonapp = NULL;
03339 }
03340
03341 if (mixmonapp) {
03342 if (!ast_strlen_zero(monitor_exec))
03343 snprintf(mixmonargs, sizeof(mixmonargs)-1, "%s|b%s|%s", tmpid2, monitor_options, monitor_exec);
03344 else
03345 snprintf(mixmonargs, sizeof(mixmonargs)-1, "%s|b%s", tmpid2, monitor_options);
03346
03347 if (option_debug)
03348 ast_log(LOG_DEBUG, "Arguments being passed to MixMonitor: %s\n", mixmonargs);
03349
03350 if (qe->chan->cdr)
03351 ast_set_flag(qe->chan->cdr, AST_CDR_FLAG_LOCKED);
03352 ret = pbx_exec(qe->chan, mixmonapp, mixmonargs);
03353 if (qe->chan->cdr)
03354 ast_clear_flag(qe->chan->cdr, AST_CDR_FLAG_LOCKED);
03355
03356 } else
03357 ast_log(LOG_WARNING, "Asked to run MixMonitor on this call, but cannot find the MixMonitor app!\n");
03358
03359 }
03360 }
03361
03362 leave_queue(qe);
03363 if (!ast_strlen_zero(url) && ast_channel_supports_html(peer)) {
03364 if (option_debug)
03365 ast_log(LOG_DEBUG, "app_queue: sendurl=%s.\n", url);
03366 ast_channel_sendurl(peer, url);
03367 }
03368 if (!ast_strlen_zero(agi)) {
03369 if (option_debug)
03370 ast_log(LOG_DEBUG, "app_queue: agi=%s.\n", agi);
03371 app = pbx_findapp("agi");
03372 if (app) {
03373 agiexec = ast_strdupa(agi);
03374 ret = pbx_exec(qe->chan, app, agiexec);
03375 } else
03376 ast_log(LOG_WARNING, "Asked to execute an AGI on this channel, but could not find application (agi)!\n");
03377 }
03378 qe->handled++;
03379 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "CONNECT", "%ld|%s", (long)time(NULL) - qe->start, peer->uniqueid);
03380 if (qe->parent->eventwhencalled)
03381 manager_event(EVENT_FLAG_AGENT, "AgentConnect",
03382 "Queue: %s\r\n"
03383 "Uniqueid: %s\r\n"
03384 "Channel: %s\r\n"
03385 "Member: %s\r\n"
03386 "MemberName: %s\r\n"
03387 "Holdtime: %ld\r\n"
03388 "BridgedChannel: %s\r\n"
03389 "%s",
03390 queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername,
03391 (long)time(NULL) - qe->start, peer->uniqueid,
03392 qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
03393 ast_copy_string(oldcontext, qe->chan->context, sizeof(oldcontext));
03394 ast_copy_string(oldexten, qe->chan->exten, sizeof(oldexten));
03395 time(&callstart);
03396
03397 if (member->status == AST_DEVICE_NOT_INUSE)
03398 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);
03399
03400 transfer_ds = setup_transfer_datastore(qe, member, callstart, callcompletedinsl);
03401 bridge = ast_bridge_call(qe->chan,peer, &bridge_config);
03402
03403 ast_channel_lock(qe->chan);
03404 if (!attended_transfer_occurred(qe->chan)) {
03405 struct ast_datastore *tds;
03406
03407
03408 if (!(qe->chan->_softhangup | peer->_softhangup) && (strcasecmp(oldcontext, qe->chan->context) || strcasecmp(oldexten, qe->chan->exten))) {
03409 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "TRANSFER", "%s|%s|%ld|%ld",
03410 qe->chan->exten, qe->chan->context, (long) (callstart - qe->start),
03411 (long) (time(NULL) - callstart));
03412 if (qe->parent->eventwhencalled)
03413 manager_event(EVENT_FLAG_AGENT, "AgentComplete",
03414 "Queue: %s\r\n"
03415 "Uniqueid: %s\r\n"
03416 "Channel: %s\r\n"
03417 "Member: %s\r\n"
03418 "MemberName: %s\r\n"
03419 "HoldTime: %ld\r\n"
03420 "TalkTime: %ld\r\n"
03421 "Reason: transfer\r\n"
03422 "%s",
03423 queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername,
03424 (long)(callstart - qe->start), (long)(time(NULL) - callstart),
03425 qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
03426 } else if (qe->chan->_softhangup) {
03427 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "COMPLETECALLER", "%ld|%ld|%d",
03428 (long) (callstart - qe->start), (long) (time(NULL) - callstart), qe->opos);
03429 if (qe->parent->eventwhencalled)
03430 manager_event(EVENT_FLAG_AGENT, "AgentComplete",
03431 "Queue: %s\r\n"
03432 "Uniqueid: %s\r\n"
03433 "Channel: %s\r\n"
03434 "Member: %s\r\n"
03435 "MemberName: %s\r\n"
03436 "HoldTime: %ld\r\n"
03437 "TalkTime: %ld\r\n"
03438 "Reason: caller\r\n"
03439 "%s",
03440 queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername,
03441 (long)(callstart - qe->start), (long)(time(NULL) - callstart),
03442 qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
03443 } else {
03444 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "COMPLETEAGENT", "%ld|%ld|%d",
03445 (long) (callstart - qe->start), (long) (time(NULL) - callstart), qe->opos);
03446 if (qe->parent->eventwhencalled)
03447 manager_event(EVENT_FLAG_AGENT, "AgentComplete",
03448 "Queue: %s\r\n"
03449 "Uniqueid: %s\r\n"
03450 "Channel: %s\r\n"
03451 "Member: %s\r\n"
03452 "MemberName: %s\r\n"
03453 "HoldTime: %ld\r\n"
03454 "TalkTime: %ld\r\n"
03455 "Reason: agent\r\n"
03456 "%s",
03457 queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername, (long)(callstart - qe->start),
03458 (long)(time(NULL) - callstart),
03459 qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
03460 }
03461 if ((tds = ast_channel_datastore_find(qe->chan, &queue_transfer_info, NULL))) {
03462 ast_channel_datastore_remove(qe->chan, tds);
03463 }
03464 update_queue(qe->parent, member, callcompletedinsl);
03465 } else {
03466 if (qe->parent->eventwhencalled)
03467 manager_event(EVENT_FLAG_AGENT, "AgentComplete",
03468 "Queue: %s\r\n"
03469 "Uniqueid: %s\r\n"
03470 "Channel: %s\r\n"
03471 "Member: %s\r\n"
03472 "MemberName: %s\r\n"
03473 "HoldTime: %ld\r\n"
03474 "TalkTime: %ld\r\n"
03475 "Reason: transfer\r\n"
03476 "%s",
03477 queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername, (long)(callstart - qe->start),
03478 (long)(time(NULL) - callstart),
03479 qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
03480 }
03481
03482 if (transfer_ds) {
03483 ast_channel_datastore_free(transfer_ds);
03484 }
03485 ast_channel_unlock(qe->chan);
03486 ast_hangup(peer);
03487 res = bridge ? bridge : 1;
03488 ao2_ref(member, -1);
03489 }
03490 out:
03491 hangupcalls(outgoing, NULL);
03492
03493 return res;
03494 }
03495
03496 static int wait_a_bit(struct queue_ent *qe)
03497 {
03498
03499 int retrywait = qe->parent->retry * 1000;
03500
03501 int res = ast_waitfordigit(qe->chan, retrywait);
03502 if (res > 0 && !valid_exit(qe, res))
03503 res = 0;
03504
03505 return res;
03506 }
03507
03508 static struct member *interface_exists(struct call_queue *q, const char *interface)
03509 {
03510 struct member *mem;
03511 struct ao2_iterator mem_iter;
03512
03513 if (!q)
03514 return NULL;
03515
03516 mem_iter = ao2_iterator_init(q->members, 0);
03517 while ((mem = ao2_iterator_next(&mem_iter))) {
03518 if (!strcasecmp(interface, mem->interface)) {
03519 ao2_iterator_destroy(&mem_iter);
03520 return mem;
03521 }
03522 ao2_ref(mem, -1);
03523 }
03524 ao2_iterator_destroy(&mem_iter);
03525
03526 return NULL;
03527 }
03528
03529
03530
03531
03532
03533
03534
03535 static void dump_queue_members(struct call_queue *pm_queue)
03536 {
03537 struct member *cur_member;
03538 char value[PM_MAX_LEN];
03539 int value_len = 0;
03540 int res;
03541 struct ao2_iterator mem_iter;
03542
03543 memset(value, 0, sizeof(value));
03544
03545 if (!pm_queue)
03546 return;
03547
03548 mem_iter = ao2_iterator_init(pm_queue->members, 0);
03549 while ((cur_member = ao2_iterator_next(&mem_iter))) {
03550 if (!cur_member->dynamic) {
03551 ao2_ref(cur_member, -1);
03552 continue;
03553 }
03554
03555 res = snprintf(value + value_len, sizeof(value) - value_len, "%s%s;%d;%d;%s;%s",
03556 value_len ? "|" : "", cur_member->interface, cur_member->penalty, cur_member->paused, cur_member->membername, cur_member->state_interface);
03557
03558 ao2_ref(cur_member, -1);
03559
03560 if (res != strlen(value + value_len)) {
03561 ast_log(LOG_WARNING, "Could not create persistent member string, out of space\n");
03562 break;
03563 }
03564 value_len += res;
03565 }
03566 ao2_iterator_destroy(&mem_iter);
03567
03568 if (value_len && !cur_member) {
03569 if (ast_db_put(pm_family, pm_queue->name, value))
03570 ast_log(LOG_WARNING, "failed to create persistent dynamic entry!\n");
03571 } else
03572
03573 ast_db_del(pm_family, pm_queue->name);
03574 }
03575
03576 static int remove_from_queue(const char *queuename, const char *interface)
03577 {
03578 struct call_queue *q;
03579 struct member *mem, tmpmem;
03580 int res = RES_NOSUCHQUEUE;
03581
03582 ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface));
03583
03584 AST_LIST_LOCK(&queues);
03585 AST_LIST_TRAVERSE(&queues, q, list) {
03586 ao2_lock(q);
03587 if (strcmp(q->name, queuename)) {
03588 ao2_unlock(q);
03589 continue;
03590 }
03591
03592 if ((mem = ao2_find(q->members, &tmpmem, OBJ_POINTER))) {
03593
03594 if (!mem->dynamic) {
03595 res = RES_NOT_DYNAMIC;
03596 ao2_ref(mem, -1);
03597 ao2_unlock(q);
03598 break;
03599 }
03600 q->membercount--;
03601 manager_event(EVENT_FLAG_AGENT, "QueueMemberRemoved",
03602 "Queue: %s\r\n"
03603 "Location: %s\r\n"
03604 "MemberName: %s\r\n",
03605 q->name, mem->interface, mem->membername);
03606 ao2_unlink(q->members, mem);
03607 remove_from_interfaces(mem->state_interface);
03608 ao2_ref(mem, -1);
03609
03610 if (queue_persistent_members)
03611 dump_queue_members(q);
03612
03613 res = RES_OKAY;
03614 } else {
03615 res = RES_EXISTS;
03616 }
03617 ao2_unlock(q);
03618 break;
03619 }
03620
03621 AST_LIST_UNLOCK(&queues);
03622
03623 return res;
03624 }
03625
03626
03627 static int add_to_queue(const char *queuename, const char *interface, const char *membername, int penalty, int paused, int dump, const char *state_interface)
03628 {
03629 struct call_queue *q;
03630 struct member *new_member, *old_member;
03631 int res = RES_NOSUCHQUEUE;
03632
03633
03634
03635 if (!(q = load_realtime_queue(queuename)))
03636 return res;
03637
03638 AST_LIST_LOCK(&queues);
03639
03640 ao2_lock(q);
03641 if ((old_member = interface_exists(q, interface)) == NULL) {
03642 if ((new_member = create_queue_member(interface, membername, penalty, paused, state_interface))) {
03643 add_to_interfaces(new_member->state_interface);
03644 new_member->dynamic = 1;
03645 ao2_link(q->members, new_member);
03646 q->membercount++;
03647 manager_event(EVENT_FLAG_AGENT, "QueueMemberAdded",
03648 "Queue: %s\r\n"
03649 "Location: %s\r\n"
03650 "MemberName: %s\r\n"
03651 "Membership: %s\r\n"
03652 "Penalty: %d\r\n"
03653 "CallsTaken: %d\r\n"
03654 "LastCall: %d\r\n"
03655 "Status: %d\r\n"
03656 "Paused: %d\r\n",
03657 q->name, new_member->interface, new_member->membername,
03658 "dynamic",
03659 new_member->penalty, new_member->calls, (int) new_member->lastcall,
03660 new_member->status, new_member->paused);
03661
03662 ao2_ref(new_member, -1);
03663 new_member = NULL;
03664
03665 if (dump)
03666 dump_queue_members(q);
03667
03668 res = RES_OKAY;
03669 } else {
03670 res = RES_OUTOFMEMORY;
03671 }
03672 } else {
03673 ao2_ref(old_member, -1);
03674 res = RES_EXISTS;
03675 }
03676 ao2_unlock(q);
03677 AST_LIST_UNLOCK(&queues);
03678
03679 return res;
03680 }
03681
03682 static int set_member_paused(const char *queuename, const char *interface, int paused)
03683 {
03684 int found = 0;
03685 struct call_queue *q;
03686 struct member *mem;
03687
03688
03689
03690 if (ast_strlen_zero(queuename))
03691 ast_queue_log("NONE", "NONE", interface, (paused ? "PAUSEALL" : "UNPAUSEALL"), "%s", "");
03692
03693 AST_LIST_LOCK(&queues);
03694 AST_LIST_TRAVERSE(&queues, q, list) {
03695 ao2_lock(q);
03696 if (ast_strlen_zero(queuename) || !strcasecmp(q->name, queuename)) {
03697 if ((mem = interface_exists(q, interface))) {
03698 found++;
03699 if (mem->paused == paused)
03700 ast_log(LOG_DEBUG, "%spausing already-%spaused queue member %s:%s\n", (paused ? "" : "un"), (paused ? "" : "un"), q->name, interface);
03701 mem->paused = paused;
03702
03703 if (queue_persistent_members)
03704 dump_queue_members(q);
03705
03706 if (mem->realtime)
03707 update_realtime_member_field(mem, q->name, "paused", paused ? "1" : "0");
03708
03709 ast_queue_log(q->name, "NONE", mem->membername, (paused ? "PAUSE" : "UNPAUSE"), "%s", "");
03710
03711 manager_event(EVENT_FLAG_AGENT, "QueueMemberPaused",
03712 "Queue: %s\r\n"
03713 "Location: %s\r\n"
03714 "MemberName: %s\r\n"
03715 "Paused: %d\r\n",
03716 q->name, mem->interface, mem->membername, paused);
03717 ao2_ref(mem, -1);
03718 }
03719 }
03720 ao2_unlock(q);
03721 }
03722 AST_LIST_UNLOCK(&queues);
03723
03724 return found ? RESULT_SUCCESS : RESULT_FAILURE;
03725 }
03726
03727
03728 static void reload_queue_members(void)
03729 {
03730 char *cur_ptr;
03731 char *queue_name;
03732 char *member;
03733 char *interface;
03734 char *membername = NULL;
03735 char *state_interface;
03736 char *penalty_tok;
03737 int penalty = 0;
03738 char *paused_tok;
03739 int paused = 0;
03740 struct ast_db_entry *db_tree;
03741 struct ast_db_entry *entry;
03742 struct call_queue *cur_queue;
03743 char queue_data[PM_MAX_LEN];
03744
03745 AST_LIST_LOCK(&queues);
03746
03747
03748 db_tree = ast_db_gettree(pm_family, NULL);
03749 for (entry = db_tree; entry; entry = entry->next) {
03750
03751 queue_name = entry->key + strlen(pm_family) + 2;
03752
03753 AST_LIST_TRAVERSE(&queues, cur_queue, list) {
03754 ao2_lock(cur_queue);
03755 if (!strcmp(queue_name, cur_queue->name))
03756 break;
03757 ao2_unlock(cur_queue);
03758 }
03759
03760 if (!cur_queue)
03761 cur_queue = load_realtime_queue(queue_name);
03762
03763 if (!cur_queue) {
03764
03765
03766 ast_log(LOG_WARNING, "Error loading persistent queue: '%s': it does not exist\n", queue_name);
03767 ast_db_del(pm_family, queue_name);
03768 continue;
03769 } else
03770 ao2_unlock(cur_queue);
03771
03772 if (ast_db_get(pm_family, queue_name, queue_data, PM_MAX_LEN))
03773 continue;
03774
03775 cur_ptr = queue_data;
03776 while ((member = strsep(&cur_ptr, "|"))) {
03777 if (ast_strlen_zero(member))
03778 continue;
03779
03780 interface = strsep(&member, ";");
03781 penalty_tok = strsep(&member, ";");
03782 paused_tok = strsep(&member, ";");
03783 membername = strsep(&member, ";");
03784 state_interface = strsep(&member,";");
03785
03786 if (!penalty_tok) {
03787 ast_log(LOG_WARNING, "Error parsing persistent member string for '%s' (penalty)\n", queue_name);
03788 break;
03789 }
03790 penalty = strtol(penalty_tok, NULL, 10);
03791 if (errno == ERANGE) {
03792 ast_log(LOG_WARNING, "Error converting penalty: %s: Out of range.\n", penalty_tok);
03793 break;
03794 }
03795
03796 if (!paused_tok) {
03797 ast_log(LOG_WARNING, "Error parsing persistent member string for '%s' (paused)\n", queue_name);
03798 break;
03799 }
03800 paused = strtol(paused_tok, NULL, 10);
03801 if ((errno == ERANGE) || paused < 0 || paused > 1) {
03802 ast_log(LOG_WARNING, "Error converting paused: %s: Expected 0 or 1.\n", paused_tok);
03803 break;
03804 }
03805 if (ast_strlen_zero(membername))
03806 membername = interface;
03807
03808 if (option_debug)
03809 ast_log(LOG_DEBUG, "Reload Members: Queue: %s Member: %s Name: %s Penalty: %d Paused: %d\n", queue_name, interface, membername, penalty, paused);
03810
03811 if (add_to_queue(queue_name, interface, membername, penalty, paused, 0, state_interface) == RES_OUTOFMEMORY) {
03812 ast_log(LOG_ERROR, "Out of Memory when reloading persistent queue member\n");
03813 break;
03814 }
03815 }
03816 }
03817
03818 AST_LIST_UNLOCK(&queues);
03819 if (db_tree) {
03820 ast_log(LOG_NOTICE, "Queue members successfully reloaded from database.\n");
03821 ast_db_freetree(db_tree);
03822 }
03823 }
03824
03825 static int pqm_exec(struct ast_channel *chan, void *data)
03826 {
03827 struct ast_module_user *lu;
03828 char *parse;
03829 int priority_jump = 0;
03830 int ignore_fail = 0;
03831 AST_DECLARE_APP_ARGS(args,
03832 AST_APP_ARG(queuename);
03833 AST_APP_ARG(interface);
03834 AST_APP_ARG(options);
03835 );
03836
03837 if (ast_strlen_zero(data)) {
03838 ast_log(LOG_WARNING, "PauseQueueMember requires an argument ([queuename]|interface[|options])\n");
03839 return -1;
03840 }
03841
03842 parse = ast_strdupa(data);
03843
03844 AST_STANDARD_APP_ARGS(args, parse);
03845
03846 lu = ast_module_user_add(chan);
03847
03848 if (args.options) {
03849 if (strchr(args.options, 'j'))
03850 priority_jump = 1;
03851 if (strchr(args.options, 'i'))
03852 ignore_fail = 1;
03853 }
03854
03855 if (ast_strlen_zero(args.interface)) {
03856 ast_log(LOG_WARNING, "Missing interface argument to PauseQueueMember ([queuename]|interface[|options])\n");
03857 ast_module_user_remove(lu);
03858 return -1;
03859 }
03860
03861 if (set_member_paused(args.queuename, args.interface, 1)) {
03862 ast_log(LOG_WARNING, "Attempt to pause interface %s, not found\n", args.interface);
03863 pbx_builtin_setvar_helper(chan, "PQMSTATUS", "NOTFOUND");
03864 if (priority_jump || ast_opt_priority_jumping) {
03865 if (!ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101)) {
03866 ast_module_user_remove(lu);
03867 return 0;
03868 }
03869 }
03870 ast_module_user_remove(lu);
03871 if (ignore_fail) {
03872 return 0;
03873 } else {
03874 return -1;
03875 }
03876 }
03877
03878 ast_module_user_remove(lu);
03879 pbx_builtin_setvar_helper(chan, "PQMSTATUS", "PAUSED");
03880 return 0;
03881 }
03882
03883 static int upqm_exec(struct ast_channel *chan, void *data)
03884 {
03885 struct ast_module_user *lu;
03886 char *parse;
03887 int priority_jump = 0;
03888 int ignore_fail = 0;
03889 AST_DECLARE_APP_ARGS(args,
03890 AST_APP_ARG(queuename);
03891 AST_APP_ARG(interface);
03892 AST_APP_ARG(options);
03893 );
03894
03895 if (ast_strlen_zero(data)) {
03896 ast_log(LOG_WARNING, "UnpauseQueueMember requires an argument ([queuename]|interface[|options])\n");
03897 return -1;
03898 }
03899
03900 parse = ast_strdupa(data);
03901
03902 AST_STANDARD_APP_ARGS(args, parse);
03903
03904 lu = ast_module_user_add(chan);
03905
03906 if (args.options) {
03907 if (strchr(args.options, 'j'))
03908 priority_jump = 1;
03909 if (strchr(args.options, 'i'))
03910 ignore_fail = 1;
03911 }
03912
03913 if (ast_strlen_zero(args.interface)) {
03914 ast_log(LOG_WARNING, "Missing interface argument to PauseQueueMember ([queuename]|interface[|options])\n");
03915 ast_module_user_remove(lu);
03916 return -1;
03917 }
03918
03919 if (set_member_paused(args.queuename, args.interface, 0)) {
03920 ast_log(LOG_WARNING, "Attempt to unpause interface %s, not found\n", args.interface);
03921 pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "NOTFOUND");
03922 if (priority_jump || ast_opt_priority_jumping) {
03923 if (!ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101)) {
03924 ast_module_user_remove(lu);
03925 return 0;
03926 }
03927 }
03928 ast_module_user_remove(lu);
03929 if (ignore_fail) {
03930 return 0;
03931 } else {
03932 return -1;
03933 }
03934 }
03935
03936 ast_module_user_remove(lu);
03937 pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "UNPAUSED");
03938 return 0;
03939 }
03940
03941 static int rqm_exec(struct ast_channel *chan, void *data)
03942 {
03943 int res=-1;
03944 struct ast_module_user *lu;
03945 char *parse, *temppos = NULL;
03946 int priority_jump = 0;
03947 AST_DECLARE_APP_ARGS(args,
03948 AST_APP_ARG(queuename);
03949 AST_APP_ARG(interface);
03950 AST_APP_ARG(options);
03951 );
03952
03953
03954 if (ast_strlen_zero(data)) {
03955 ast_log(LOG_WARNING, "RemoveQueueMember requires an argument (queuename[|interface[|options]])\n");
03956 return -1;
03957 }
03958
03959 parse = ast_strdupa(data);
03960
03961 AST_STANDARD_APP_ARGS(args, parse);
03962
03963 lu = ast_module_user_add(chan);
03964
03965 if (ast_strlen_zero(args.interface)) {
03966 args.interface = ast_strdupa(chan->name);
03967 temppos = strrchr(args.interface, '-');
03968 if (temppos)
03969 *temppos = '\0';
03970 }
03971
03972 if (args.options) {
03973 if (strchr(args.options, 'j'))
03974 priority_jump = 1;
03975 }
03976
03977 switch (remove_from_queue(args.queuename, args.interface)) {
03978 case RES_OKAY:
03979 ast_queue_log(args.queuename, chan->uniqueid, args.interface, "REMOVEMEMBER", "%s", "");
03980 ast_log(LOG_NOTICE, "Removed interface '%s' from queue '%s'\n", args.interface, args.queuename);
03981 pbx_builtin_setvar_helper(chan, "RQMSTATUS", "REMOVED");
03982 res = 0;
03983 break;
03984 case RES_EXISTS:
03985 ast_log(LOG_DEBUG, "Unable to remove interface '%s' from queue '%s': Not there\n", args.interface, args.queuename);
03986 if (priority_jump || ast_opt_priority_jumping)
03987 ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101);
03988 pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOTINQUEUE");
03989 res = 0;
03990 break;
03991 case RES_NOSUCHQUEUE:
03992 ast_log(LOG_WARNING, "Unable to remove interface from queue '%s': No such queue\n", args.queuename);
03993 pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOSUCHQUEUE");
03994 res = 0;
03995 break;
03996 case RES_NOT_DYNAMIC:
03997 ast_log(LOG_WARNING, "Unable to remove interface from queue '%s': '%s' is not a dynamic member\n", args.queuename, args.interface);
03998 pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOTDYNAMIC");
03999 res = 0;
04000 break;
04001 }
04002
04003 ast_module_user_remove(lu);
04004
04005 return res;
04006 }
04007
04008 static int aqm_exec(struct ast_channel *chan, void *data)
04009 {
04010 int res=-1;
04011 struct ast_module_user *lu;
04012 char *parse, *temppos = NULL;
04013 int priority_jump = 0;
04014 AST_DECLARE_APP_ARGS(args,
04015 AST_APP_ARG(queuename);
04016 AST_APP_ARG(interface);
04017 AST_APP_ARG(penalty);
04018 AST_APP_ARG(options);
04019 AST_APP_ARG(membername);
04020 AST_APP_ARG(state_interface);
04021 );
04022 int penalty = 0;
04023
04024 if (ast_strlen_zero(data)) {
04025 ast_log(LOG_WARNING, "AddQueueMember requires an argument (queuename[|interface[|penalty[|options[|membername[|state_interface]]]]])\n");
04026 return -1;
04027 }
04028
04029 parse = ast_strdupa(data);
04030
04031 AST_STANDARD_APP_ARGS(args, parse);
04032
04033 lu = ast_module_user_add(chan);
04034
04035 if (ast_strlen_zero(args.interface)) {
04036 args.interface = ast_strdupa(chan->name);
04037 temppos = strrchr(args.interface, '-');
04038 if (temppos)
04039 *temppos = '\0';
04040 }
04041
04042 if (!ast_strlen_zero(args.penalty)) {
04043 if ((sscanf(args.penalty, "%30d", &penalty) != 1) || penalty < 0) {
04044 ast_log(LOG_WARNING, "Penalty '%s' is invalid, must be an integer >= 0\n", args.penalty);
04045 penalty = 0;
04046 }
04047 }
04048
04049 if (args.options) {
04050 if (strchr(args.options, 'j'))
04051 priority_jump = 1;
04052 }
04053
04054 switch (add_to_queue(args.queuename, args.interface, args.membername, penalty, 0, queue_persistent_members, args.state_interface)) {
04055 case RES_OKAY:
04056 ast_queue_log(args.queuename, chan->uniqueid, args.interface, "ADDMEMBER", "%s", "");
04057 ast_log(LOG_NOTICE, "Added interface '%s' to queue '%s'\n", args.interface, args.queuename);
04058 pbx_builtin_setvar_helper(chan, "AQMSTATUS", "ADDED");
04059 res = 0;
04060 break;
04061 case RES_EXISTS:
04062 ast_log(LOG_WARNING, "Unable to add interface '%s' to queue '%s': Already there\n", args.interface, args.queuename);
04063 if (priority_jump || ast_opt_priority_jumping)
04064 ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101);
04065 pbx_builtin_setvar_helper(chan, "AQMSTATUS", "MEMBERALREADY");
04066 res = 0;
04067 break;
04068 case RES_NOSUCHQUEUE:
04069 ast_log(LOG_WARNING, "Unable to add interface to queue '%s': No such queue\n", args.queuename);
04070 pbx_builtin_setvar_helper(chan, "AQMSTATUS", "NOSUCHQUEUE");
04071 res = 0;
04072 break;
04073 case RES_OUTOFMEMORY:
04074 ast_log(LOG_ERROR, "Out of memory adding member %s to queue %s\n", args.interface, args.queuename);
04075 break;
04076 }
04077
04078 ast_module_user_remove(lu);
04079
04080 return res;
04081 }
04082
04083 static int ql_exec(struct ast_channel *chan, void *data)
04084 {
04085 struct ast_module_user *u;
04086 char *parse;
04087
04088 AST_DECLARE_APP_ARGS(args,
04089 AST_APP_ARG(queuename);
04090 AST_APP_ARG(uniqueid);
04091 AST_APP_ARG(membername);
04092 AST_APP_ARG(event);
04093 AST_APP_ARG(params);
04094 );
04095
04096 if (ast_strlen_zero(data)) {
04097 ast_log(LOG_WARNING, "QueueLog requires arguments (queuename|uniqueid|membername|event[|additionalinfo]\n");
04098 return -1;
04099 }
04100
04101 u = ast_module_user_add(chan);
04102
04103 parse = ast_strdupa(data);
04104
04105 AST_STANDARD_APP_ARGS(args, parse);
04106
04107 if (ast_strlen_zero(args.queuename) || ast_strlen_zero(args.uniqueid)
04108 || ast_strlen_zero(args.membername) || ast_strlen_zero(args.event)) {
04109 ast_log(LOG_WARNING, "QueueLog requires arguments (queuename|uniqueid|membername|event[|additionalinfo])\n");
04110 ast_module_user_remove(u);
04111 return -1;
04112 }
04113
04114 ast_queue_log(args.queuename, args.uniqueid, args.membername, args.event,
04115 "%s", args.params ? args.params : "");
04116
04117 ast_module_user_remove(u);
04118
04119 return 0;
04120 }
04121
04122
04123
04124
04125
04126
04127
04128
04129
04130
04131
04132
04133
04134 static int queue_exec(struct ast_channel *chan, void *data)
04135 {
04136 int res=-1;
04137 int ringing=0;
04138 struct ast_module_user *lu;
04139 const char *user_priority;
04140 const char *max_penalty_str;
04141 int prio;
04142 int max_penalty;
04143 enum queue_result reason = QUEUE_UNKNOWN;
04144
04145 int tries = 0;
04146 int noption = 0;
04147 char *parse;
04148 AST_DECLARE_APP_ARGS(args,
04149 AST_APP_ARG(queuename);
04150 AST_APP_ARG(options);
04151 AST_APP_ARG(url);
04152 AST_APP_ARG(announceoverride);
04153 AST_APP_ARG(queuetimeoutstr);
04154 AST_APP_ARG(agi);
04155 );
04156
04157 struct queue_ent qe = { 0 };
04158
04159 if (ast_strlen_zero(data)) {
04160 ast_log(LOG_WARNING, "Queue requires an argument: queuename[|options[|URL[|announceoverride[|timeout[|agi]]]]]\n");
04161 return -1;
04162 }
04163
04164 parse = ast_strdupa(data);
04165 AST_STANDARD_APP_ARGS(args, parse);
04166
04167 lu = ast_module_user_add(chan);
04168
04169
04170 qe.start = time(NULL);
04171
04172
04173 if (!ast_strlen_zero(args.queuetimeoutstr))
04174 qe.expire = qe.start + atoi(args.queuetimeoutstr);
04175 else
04176 qe.expire = 0;
04177
04178
04179 user_priority = pbx_builtin_getvar_helper(chan, "QUEUE_PRIO");
04180 if (user_priority) {
04181 if (sscanf(user_priority, "%30d", &prio) == 1) {
04182 if (option_debug)
04183 ast_log(LOG_DEBUG, "%s: Got priority %d from ${QUEUE_PRIO}.\n",
04184 chan->name, prio);
04185 } else {
04186 ast_log(LOG_WARNING, "${QUEUE_PRIO}: Invalid value (%s), channel %s.\n",
04187 user_priority, chan->name);
04188 prio = 0;
04189 }
04190 } else {
04191 if (option_debug > 2)
04192 ast_log(LOG_DEBUG, "NO QUEUE_PRIO variable found. Using default.\n");
04193 prio = 0;
04194 }
04195
04196
04197 if ((max_penalty_str = pbx_builtin_getvar_helper(chan, "QUEUE_MAX_PENALTY"))) {
04198 if (sscanf(max_penalty_str, "%30d", &max_penalty) == 1) {
04199 if (option_debug)
04200 ast_log(LOG_DEBUG, "%s: Got max penalty %d from ${QUEUE_MAX_PENALTY}.\n",
04201 chan->name, max_penalty);
04202 } else {
04203 ast_log(LOG_WARNING, "${QUEUE_MAX_PENALTY}: Invalid value (%s), channel %s.\n",
04204 max_penalty_str, chan->name);
04205 max_penalty = 0;
04206 }
04207 } else {
04208 max_penalty = 0;
04209 }
04210
04211 if (args.options && (strchr(args.options, 'r')))
04212 ringing = 1;
04213
04214 if (ringing != 1 && args.options && (strchr(args.options, 'R'))) {
04215 qe.ring_when_ringing = 1;
04216 }
04217
04218 if (option_debug)
04219 ast_log(LOG_DEBUG, "queue: %s, options: %s, url: %s, announce: %s, expires: %ld, priority: %d\n",
04220 args.queuename, args.options, args.url, args.announceoverride, (long)qe.expire, prio);
04221
04222 qe.chan = chan;
04223 qe.prio = prio;
04224 qe.max_penalty = max_penalty;
04225 qe.last_pos_said = 0;
04226 qe.last_pos = 0;
04227 qe.last_periodic_announce_time = time(NULL);
04228 qe.last_periodic_announce_sound = 0;
04229 qe.valid_digits = 0;
04230 if (!join_queue(args.queuename, &qe, &reason)) {
04231 int makeannouncement = 0;
04232
04233 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "ENTERQUEUE", "%s|%s", S_OR(args.url, ""),
04234 S_OR(chan->cid.cid_num, ""));
04235 check_turns:
04236 if (ringing) {
04237 ast_indicate(chan, AST_CONTROL_RINGING);
04238 } else {
04239 ast_moh_start(chan, qe.moh, NULL);
04240 }
04241
04242
04243 res = wait_our_turn(&qe, ringing, &reason);
04244 if (res)
04245 goto stop;
04246
04247 for (;;) {
04248
04249
04250
04251
04252
04253 enum queue_member_status stat;
04254
04255
04256 if (qe.expire && (time(NULL) >= qe.expire)) {
04257 record_abandoned(&qe);
04258 reason = QUEUE_TIMEOUT;
04259 res = 0;
04260 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
04261 break;
04262 }
04263
04264 if (makeannouncement) {
04265
04266 if (qe.parent->announcefrequency && !ringing)
04267 if ((res = say_position(&qe)))
04268 goto stop;
04269
04270 }
04271 makeannouncement = 1;
04272
04273
04274 if (qe.expire && (time(NULL) >= qe.expire)) {
04275 record_abandoned(&qe);
04276 reason = QUEUE_TIMEOUT;
04277 res = 0;
04278 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
04279 break;
04280 }
04281
04282 if (qe.parent->periodicannouncefrequency && !ringing)
04283 if ((res = say_periodic_announcement(&qe)))
04284 goto stop;
04285
04286
04287 if (qe.expire && (time(NULL) >= qe.expire)) {
04288 record_abandoned(&qe);
04289 reason = QUEUE_TIMEOUT;
04290 res = 0;
04291 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
04292 break;
04293 }
04294
04295 res = try_calling(&qe, args.options, args.announceoverride, args.url, &tries, &noption, args.agi);
04296 if (res)
04297 goto stop;
04298
04299 stat = get_member_status(qe.parent, qe.max_penalty);
04300
04301
04302 if (noption && tries >= qe.parent->membercount) {
04303 if (option_verbose > 2)
04304 ast_verbose(VERBOSE_PREFIX_3 "Exiting on time-out cycle\n");
04305 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
04306 record_abandoned(&qe);
04307 reason = QUEUE_TIMEOUT;
04308 res = 0;
04309 break;
04310 }
04311
04312
04313 if (qe.parent->leavewhenempty && (stat == QUEUE_NO_MEMBERS)) {
04314 record_abandoned(&qe);
04315 reason = QUEUE_LEAVEEMPTY;
04316 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe.pos, qe.opos, (long)(time(NULL) - qe.start));
04317 res = 0;
04318 break;
04319 }
04320
04321
04322 if ((qe.parent->leavewhenempty == QUEUE_EMPTY_STRICT) && (stat == QUEUE_NO_REACHABLE_MEMBERS)) {
04323 record_abandoned(&qe);
04324 reason = QUEUE_LEAVEUNAVAIL;
04325 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe.pos, qe.opos, (long)(time(NULL) - qe.start));
04326 res = 0;
04327 break;
04328 }
04329
04330
04331 if (qe.expire && (time(NULL) >= qe.expire)) {
04332 record_abandoned(&qe);
04333 reason = QUEUE_TIMEOUT;
04334 res = 0;
04335 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
04336 break;
04337 }
04338
04339
04340 update_realtime_members(qe.parent);
04341
04342
04343 res = wait_a_bit(&qe);
04344 if (res)
04345 goto stop;
04346
04347
04348
04349
04350
04351 if (!is_our_turn(&qe)) {
04352 if (option_debug)
04353 ast_log(LOG_DEBUG, "Darn priorities, going back in queue (%s)!\n",
04354 qe.chan->name);
04355 goto check_turns;
04356 }
04357 }
04358
04359 stop:
04360 if (res) {
04361 if (res < 0) {
04362 if (!qe.handled) {
04363 record_abandoned(&qe);
04364 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "ABANDON",
04365 "%d|%d|%ld", qe.pos, qe.opos,
04366 (long) time(NULL) - qe.start);
04367 }
04368 res = -1;
04369 } else if (qe.valid_digits) {
04370 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHKEY",
04371 "%s|%d", qe.digits, qe.pos);
04372 }
04373 }
04374
04375
04376 if (res >= 0) {
04377 res = 0;
04378 if (ringing) {
04379 ast_indicate(chan, -1);
04380 } else {
04381 ast_moh_stop(chan);
04382 }
04383 ast_stopstream(chan);
04384 }
04385 leave_queue(&qe);
04386 if (reason != QUEUE_UNKNOWN)
04387 set_queue_result(chan, reason);
04388 } else {
04389 ast_log(LOG_WARNING, "Unable to join queue '%s'\n", args.queuename);
04390 set_queue_result(chan, reason);
04391 res = 0;
04392 }
04393 if (qe.parent) {
04394
04395
04396
04397 ao2_ref(qe.parent, -1);
04398 }
04399 ast_module_user_remove(lu);
04400
04401 return res;
04402 }
04403
04404 enum qmc_status {
04405 QMC_VALID = 0,
04406 QMC_PAUSED,
04407 QMC_ACTIVE,
04408 QMC_FREE,
04409 QMC_ALL
04410 };
04411
04412 static int queue_function_queuemembercount(struct ast_channel *chan, char *cmd, char *data, char *buf, size_t len)
04413 {
04414 int count = 0;
04415 struct call_queue *q;
04416 struct ast_module_user *lu;
04417 struct member *m;
04418 struct ao2_iterator mem_iter;
04419 char *name, *item;
04420 enum qmc_status mode = QMC_VALID;
04421
04422 buf[0] = '\0';
04423
04424 if (ast_strlen_zero(data)) {
04425 ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
04426 return -1;
04427 }
04428
04429 name = ast_strdupa(data);
04430
04431 lu = ast_module_user_add(chan);
04432
04433 if ((item = strchr(name, ':'))) {
04434 *item = '\0';
04435 item++;
04436 } else {
04437 item = "";
04438 }
04439
04440 if (!strcasecmp(item, "valid")) {
04441 mode = QMC_VALID;
04442 } else if (!strcasecmp(item, "paused")) {
04443 mode = QMC_PAUSED;
04444 } else if (!strcasecmp(item, "active")) {
04445 mode = QMC_ACTIVE;
04446 } else if (!strcasecmp(item, "free")) {
04447 mode = QMC_FREE;
04448 } else if (!strcasecmp(item, "all")) {
04449 mode = QMC_ALL;
04450 }
04451
04452 if ((q = load_realtime_queue(name))) {
04453 ao2_lock(q);
04454 mem_iter = ao2_iterator_init(q->members, 0);
04455 while ((m = ao2_iterator_next(&mem_iter))) {
04456 switch (mode) {
04457 case QMC_VALID:
04458
04459 if ((m->status != AST_DEVICE_UNAVAILABLE) && (m->status != AST_DEVICE_INVALID)) {
04460 count++;
04461 }
04462 break;
04463 case QMC_PAUSED:
04464
04465 if (m->paused) {
04466 count++;
04467 }
04468 break;
04469 case QMC_ACTIVE:
04470
04471 if (!m->paused && (m->status != AST_DEVICE_UNAVAILABLE) && (m->status != AST_DEVICE_INVALID)) {
04472 count++;
04473 }
04474 break;
04475 case QMC_FREE:
04476
04477 if (!m->paused && ((m->status == AST_DEVICE_UNKNOWN) || (m->status == AST_DEVICE_NOT_INUSE))) {
04478 count++;
04479 }
04480 break;
04481 default:
04482 count++;
04483 break;
04484 }
04485 ao2_ref(m, -1);
04486 }
04487 ao2_iterator_destroy(&mem_iter);
04488 ao2_unlock(q);
04489 } else
04490 ast_log(LOG_WARNING, "queue %s was not found\n", name);
04491
04492 snprintf(buf, len, "%d", count);
04493 ast_module_user_remove(lu);
04494
04495 return 0;
04496 }
04497
04498 static int queue_function_queuewaitingcount(struct ast_channel *chan, char *cmd, char *data, char *buf, size_t len)
04499 {
04500 int count = 0;
04501 struct call_queue *q;
04502 struct ast_module_user *lu;
04503 struct ast_variable *var = NULL;
04504
04505 buf[0] = '\0';
04506
04507 if (ast_strlen_zero(data)) {
04508 ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
04509 return -1;
04510 }
04511
04512 lu = ast_module_user_add(chan);
04513
04514 AST_LIST_LOCK(&queues);
04515 AST_LIST_TRAVERSE(&queues, q, list) {
04516 if (!strcasecmp(q->name, data)) {
04517 ao2_lock(q);
04518 break;
04519 }
04520 }
04521 AST_LIST_UNLOCK(&queues);
04522
04523 if (q) {
04524 count = q->count;
04525 ao2_unlock(q);
04526 } else if ((var = ast_load_realtime("queues", "name", data, NULL))) {
04527
04528
04529
04530
04531 count = 0;
04532 ast_variables_destroy(var);
04533 } else
04534 ast_log(LOG_WARNING, "queue %s was not found\n", data);
04535
04536 snprintf(buf, len, "%d", count);
04537 ast_module_user_remove(lu);
04538 return 0;
04539 }
04540
04541 static int queue_function_queuememberlist(struct ast_channel *chan, char *cmd, char *data, char *buf, size_t len)
04542 {
04543 struct ast_module_user *u;
04544 struct call_queue *q;
04545 struct member *m;
04546
04547
04548 buf[0] = '\0';
04549
04550 if (ast_strlen_zero(data)) {
04551 ast_log(LOG_ERROR, "QUEUE_MEMBER_LIST requires an argument: queuename\n");
04552 return -1;
04553 }
04554
04555 u = ast_module_user_add(chan);
04556
04557 AST_LIST_LOCK(&queues);
04558 AST_LIST_TRAVERSE(&queues, q, list) {
04559 if (!strcasecmp(q->name, data)) {
04560 ao2_lock(q);
04561 break;
04562 }
04563 }
04564 AST_LIST_UNLOCK(&queues);
04565
04566 if (q) {
04567 int buflen = 0, count = 0;
04568 struct ao2_iterator mem_iter = ao2_iterator_init(q->members, 0);
04569
04570 while ((m = ao2_iterator_next(&mem_iter))) {
04571
04572 if (count++) {
04573 strncat(buf + buflen, ",", len - buflen - 1);
04574 buflen++;
04575 }
04576 strncat(buf + buflen, m->interface, len - buflen - 1);
04577 buflen += strlen(m->interface);
04578
04579 if (buflen >= len - 2) {
04580 ao2_ref(m, -1);
04581 ast_log(LOG_WARNING, "Truncating list\n");
04582 break;
04583 }
04584 ao2_ref(m, -1);
04585 }
04586 ao2_iterator_destroy(&mem_iter);
04587 ao2_unlock(q);
04588 } else
04589 ast_log(LOG_WARNING, "queue %s was not found\n", data);
04590
04591
04592 buf[len - 1] = '\0';
04593 ast_module_user_remove(u);
04594
04595 return 0;
04596 }
04597
04598 static struct ast_custom_function queueagentcount_function = {
04599 .name = "QUEUEAGENTCOUNT",
04600 .synopsis = "Count number of agents answering a queue",
04601 .syntax = "QUEUEAGENTCOUNT(<queuename>)",
04602 .desc =
04603 "Returns the number of members currently associated with the specified queue.\n"
04604 "This function is deprecated. You should use QUEUE_MEMBER_COUNT() instead.\n",
04605 .read = queue_function_queuemembercount,
04606 };
04607
04608 static struct ast_custom_function queuemembercount_function = {
04609 .name = "QUEUE_MEMBER_COUNT",
04610 .synopsis = "Count number of members answering a queue",
04611 .syntax = "QUEUE_MEMBER_COUNT(<queuename>[:mode])",
04612 .desc =
04613 "Returns the number of members currently associated with the specified queue.\n"
04614 "Valid mode values are:\n"
04615 "- valid (default) Members anwering the queue.\n"
04616 "- paused Paused members.\n"
04617 "- active Active members(ie. valid and not paused).\n"
04618 "- free Active members not in use.\n"
04619 "- all All members defined in queue.\n",
04620 .read = queue_function_queuemembercount,
04621 };
04622
04623 static struct ast_custom_function queuewaitingcount_function = {
04624 .name = "QUEUE_WAITING_COUNT",
04625 .synopsis = "Count number of calls currently waiting in a queue",
04626 .syntax = "QUEUE_WAITING_COUNT(<queuename>)",
04627 .desc =
04628 "Returns the number of callers currently waiting in the specified queue.\n",
04629 .read = queue_function_queuewaitingcount,
04630 };
04631
04632 static struct ast_custom_function queuememberlist_function = {
04633 .name = "QUEUE_MEMBER_LIST",
04634 .synopsis = "Returns a list of interfaces on a queue",
04635 .syntax = "QUEUE_MEMBER_LIST(<queuename>)",
04636 .desc =
04637 "Returns a comma-separated list of members associated with the specified queue.\n",
04638 .read = queue_function_queuememberlist,
04639 };
04640
04641 static int reload_queues(void)
04642 {
04643 struct call_queue *q;
04644 struct ast_config *cfg;
04645 char *cat, *tmp;
04646 struct ast_variable *var;
04647 struct member *cur, *newm;
04648 struct ao2_iterator mem_iter;
04649 int new;
04650 const char *general_val = NULL;
04651 char *parse;
04652 char *interface, *state_interface;
04653 char *membername = NULL;
04654 int penalty;
04655 AST_DECLARE_APP_ARGS(args,
04656 AST_APP_ARG(interface);
04657 AST_APP_ARG(penalty);
04658 AST_APP_ARG(membername);
04659 AST_APP_ARG(state_interface);
04660 );
04661
04662 if (!(cfg = ast_config_load("queues.conf"))) {
04663 ast_log(LOG_NOTICE, "No call queueing config file (queues.conf), so no call queues\n");
04664 return 0;
04665 }
04666 AST_LIST_LOCK(&queues);
04667 use_weight=0;
04668
04669 AST_LIST_TRAVERSE(&queues, q, list) {
04670 if (!q->realtime) {
04671 q->dead = 1;
04672 q->found = 0;
04673 }
04674 }
04675
04676
04677 cat = NULL;
04678 while ((cat = ast_category_browse(cfg, cat)) ) {
04679 if (!strcasecmp(cat, "general")) {
04680
04681 queue_debug = 0;
04682 if ((general_val = ast_variable_retrieve(cfg, "general", "debug")))
04683 queue_debug = ast_true(general_val);
04684 queue_persistent_members = 0;
04685 if ((general_val = ast_variable_retrieve(cfg, "general", "persistentmembers")))
04686 queue_persistent_members = ast_true(general_val);
04687 autofill_default = 0;
04688 if ((general_val = ast_variable_retrieve(cfg, "general", "autofill")))
04689 autofill_default = ast_true(general_val);
04690 montype_default = 0;
04691 if ((general_val = ast_variable_retrieve(cfg, "general", "monitor-type")))
04692 if (!strcasecmp(general_val, "mixmonitor"))
04693 montype_default = 1;
04694 } else {
04695
04696 AST_LIST_TRAVERSE(&queues, q, list) {
04697 if (!strcmp(q->name, cat))
04698 break;
04699 }
04700 if (!q) {
04701
04702 if (!(q = alloc_queue(cat))) {
04703
04704 }
04705 new = 1;
04706 } else
04707 new = 0;
04708 if (q) {
04709 const char *tmpvar;
04710 if (!new)
04711 ao2_lock(q);
04712
04713 if (q->found) {
04714 ast_log(LOG_WARNING, "Queue '%s' already defined! Skipping!\n", cat);
04715 if (!new)
04716 ao2_unlock(q);
04717 continue;
04718 }
04719
04720
04721
04722
04723
04724
04725 if ((tmpvar = ast_variable_retrieve(cfg, cat, "strategy"))) {
04726 q->strategy = strat2int(tmpvar);
04727 if (q->strategy < 0) {
04728 ast_log(LOG_WARNING, "'%s' isn't a valid strategy for queue '%s', using ringall instead\n", tmpvar, q->name);
04729 q->strategy = QUEUE_STRATEGY_RINGALL;
04730 }
04731 } else {
04732 q->strategy = QUEUE_STRATEGY_RINGALL;
04733 }
04734
04735
04736 init_queue(q);
04737 clear_queue(q);
04738 mem_iter = ao2_iterator_init(q->members, 0);
04739 while ((cur = ao2_iterator_next(&mem_iter))) {
04740 if (!cur->dynamic) {
04741 cur->delme = 1;
04742 }
04743 ao2_ref(cur, -1);
04744 }
04745 ao2_iterator_destroy(&mem_iter);
04746 for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
04747 if (!strcasecmp(var->name, "member")) {
04748 struct member tmpmem;
04749 membername = NULL;
04750
04751 if (ast_strlen_zero(var->value)) {
04752 ast_log(LOG_WARNING, "Empty queue member definition at line %d. Moving on!\n", var->lineno);
04753 continue;
04754 }
04755
04756
04757 if (!(parse = ast_strdup(var->value))) {
04758 continue;
04759 }
04760
04761 AST_NONSTANDARD_APP_ARGS(args, parse, ',');
04762
04763 interface = args.interface;
04764 if (!ast_strlen_zero(args.penalty)) {
04765 tmp = ast_skip_blanks(args.penalty);
04766 penalty = atoi(tmp);
04767 if (penalty < 0) {
04768 penalty = 0;
04769 }
04770 } else
04771 penalty = 0;
04772
04773 if (!ast_strlen_zero(args.membername)) {
04774 membername = ast_skip_blanks(args.membername);
04775 }
04776
04777 if (!ast_strlen_zero(args.state_interface)) {
04778 state_interface = ast_skip_blanks(args.state_interface);
04779 } else {
04780 state_interface = interface;
04781 }
04782
04783
04784 ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface));
04785 cur = ao2_find(q->members, &tmpmem, OBJ_POINTER | OBJ_UNLINK);
04786
04787
04788 if (cur && strcasecmp(cur->state_interface, state_interface)) {
04789 remove_from_interfaces(cur->state_interface);
04790 }
04791
04792 newm = create_queue_member(interface, membername, penalty, cur ? cur->paused : 0, state_interface);
04793 if (!cur || (cur && strcasecmp(cur->state_interface, state_interface))) {
04794 add_to_interfaces(state_interface);
04795 }
04796 ao2_link(q->members, newm);
04797 ao2_ref(newm, -1);
04798 newm = NULL;
04799
04800 if (cur)
04801 ao2_ref(cur, -1);
04802 else {
04803 q->membercount++;
04804 }
04805 ast_free(parse);
04806 } else {
04807 queue_set_param(q, var->name, var->value, var->lineno, 1);
04808 }
04809 }
04810
04811
04812 mem_iter = ao2_iterator_init(q->members, 0);
04813 while ((cur = ao2_iterator_next(&mem_iter))) {
04814 if (! cur->delme) {
04815 ao2_ref(cur, -1);
04816 continue;
04817 }
04818
04819 q->membercount--;
04820 ao2_unlink(q->members, cur);
04821 remove_from_interfaces(cur->state_interface);
04822 ao2_ref(cur, -1);
04823 }
04824 ao2_iterator_destroy(&mem_iter);
04825
04826 if (q->strategy == QUEUE_STRATEGY_ROUNDROBIN)
04827 rr_dep_warning();
04828
04829 if (new) {
04830 AST_LIST_INSERT_HEAD(&queues, q, list);
04831 } else
04832 ao2_unlock(q);
04833 }
04834 }
04835 }
04836 ast_config_destroy(cfg);
04837 AST_LIST_TRAVERSE_SAFE_BEGIN(&queues, q, list) {
04838 if (q->dead) {
04839 AST_LIST_REMOVE_CURRENT(&queues, list);
04840 ao2_ref(q, -1);
04841 } else {
04842 ao2_lock(q);
04843 mem_iter = ao2_iterator_init(q->members, 0);
04844 while ((cur = ao2_iterator_next(&mem_iter))) {
04845 if (cur->dynamic)
04846 q->membercount++;
04847 cur->status = ast_device_state(cur->state_interface);
04848 ao2_ref(cur, -1);
04849 }
04850 ao2_iterator_destroy(&mem_iter);
04851 ao2_unlock(q);
04852 }
04853 }
04854 AST_LIST_TRAVERSE_SAFE_END;
04855 AST_LIST_UNLOCK(&queues);
04856 return 1;
04857 }
04858
04859 static int __queues_show(struct mansession *s, int manager, int fd, int argc, char **argv)
04860 {
04861 struct call_queue *q;
04862 struct queue_ent *qe;
04863 struct member *mem;
04864 int pos, queue_show;
04865 time_t now;
04866 char max_buf[150];
04867 char *max;
04868 size_t max_left;
04869 float sl = 0;
04870 char *term = manager ? "\r\n" : "\n";
04871 struct ao2_iterator mem_iter;
04872
04873 time(&now);
04874 if (argc == 2)
04875 queue_show = 0;
04876 else if (argc == 3)
04877 queue_show = 1;
04878 else
04879 return RESULT_SHOWUSAGE;
04880
04881
04882 if (queue_show) {
04883 load_realtime_queue(argv[2]);
04884 } else if (ast_check_realtime("queues")) {
04885 struct ast_config *cfg = ast_load_realtime_multientry("queues", "name LIKE", "%", (char *) NULL);
04886 char *queuename;
04887 if (cfg) {
04888 for (queuename = ast_category_browse(cfg, NULL); !ast_strlen_zero(queuename); queuename = ast_category_browse(cfg, queuename)) {
04889 load_realtime_queue(queuename);
04890 }
04891 ast_config_destroy(cfg);
04892 }
04893 }
04894
04895 AST_LIST_LOCK(&queues);
04896 if (AST_LIST_EMPTY(&queues)) {
04897 AST_LIST_UNLOCK(&queues);
04898 if (queue_show) {
04899 if (s)
04900 astman_append(s, "No such queue: %s.%s",argv[2], term);
04901 else
04902 ast_cli(fd, "No such queue: %s.%s",argv[2], term);
04903 } else {
04904 if (s)
04905 astman_append(s, "No queues.%s", term);
04906 else
04907 ast_cli(fd, "No queues.%s", term);
04908 }
04909 return RESULT_SUCCESS;
04910 }
04911 AST_LIST_TRAVERSE(&queues, q, list) {
04912 ao2_lock(q);
04913 if (queue_show) {
04914 if (strcasecmp(q->name, argv[2]) != 0) {
04915 ao2_unlock(q);
04916 if (!AST_LIST_NEXT(q, list)) {
04917 ast_cli(fd, "No such queue: %s.%s",argv[2], term);
04918 break;
04919 }
04920 continue;
04921 }
04922 }
04923 max_buf[0] = '\0';
04924 max = max_buf;
04925 max_left = sizeof(max_buf);
04926 if (q->maxlen)
04927 ast_build_string(&max, &max_left, "%d", q->maxlen);
04928 else
04929 ast_build_string(&max, &max_left, "unlimited");
04930 sl = 0;
04931 if (q->callscompleted > 0)
04932 sl = 100 * ((float) q->callscompletedinsl / (float) q->callscompleted);
04933 if (s)
04934 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",
04935 q->name, q->count, max_buf, int2strat(q->strategy), q->holdtime, q->ringlimit,
04936 q->weight, q->callscompleted, q->callsabandoned, sl, q->servicelevel, term);
04937 else
04938 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",
04939 q->name, q->count, max_buf, int2strat(q->strategy), q->holdtime, q->ringlimit,
04940 q->weight, q->callscompleted, q->callsabandoned, sl, q->servicelevel, term);
04941 if (ao2_container_count(q->members)) {
04942 if (s)
04943 astman_append(s, " Members: %s", term);
04944 else
04945 ast_cli(fd, " Members: %s", term);
04946 mem_iter = ao2_iterator_init(q->members, 0);
04947 while ((mem = ao2_iterator_next(&mem_iter))) {
04948 max_buf[0] = '\0';
04949 max = max_buf;
04950 max_left = sizeof(max_buf);
04951 if (strcasecmp(mem->membername, mem->interface)) {
04952 ast_build_string(&max, &max_left, " (%s)", mem->interface);
04953 }
04954 if (mem->penalty)
04955 ast_build_string(&max, &max_left, " with penalty %d", mem->penalty);
04956 if (mem->dynamic)
04957 ast_build_string(&max, &max_left, " (dynamic)");
04958 if (mem->realtime)
04959 ast_build_string(&max, &max_left, " (realtime)");
04960 if (mem->paused)
04961 ast_build_string(&max, &max_left, " (paused)");
04962 ast_build_string(&max, &max_left, " (%s)", devstate2str(mem->status));
04963 if (mem->calls) {
04964 ast_build_string(&max, &max_left, " has taken %d calls (last was %ld secs ago)",
04965 mem->calls, (long) (time(NULL) - mem->lastcall));
04966 } else
04967 ast_build_string(&max, &max_left, " has taken no calls yet");
04968 if (s)
04969 astman_append(s, " %s%s%s", mem->membername, max_buf, term);
04970 else
04971 ast_cli(fd, " %s%s%s", mem->membername, max_buf, term);
04972 ao2_ref(mem, -1);
04973 }
04974 ao2_iterator_destroy(&mem_iter);
04975 } else if (s)
04976 astman_append(s, " No Members%s", term);
04977 else
04978 ast_cli(fd, " No Members%s", term);
04979 if (q->head) {
04980 pos = 1;
04981 if (s)
04982 astman_append(s, " Callers: %s", term);
04983 else
04984 ast_cli(fd, " Callers: %s", term);
04985 for (qe = q->head; qe; qe = qe->next) {
04986 if (s)
04987 astman_append(s, " %d. %s (wait: %ld:%2.2ld, prio: %d)%s",
04988 pos++, qe->chan->name, (long) (now - qe->start) / 60,
04989 (long) (now - qe->start) % 60, qe->prio, term);
04990 else
04991 ast_cli(fd, " %d. %s (wait: %ld:%2.2ld, prio: %d)%s", pos++,
04992 qe->chan->name, (long) (now - qe->start) / 60,
04993 (long) (now - qe->start) % 60, qe->prio, term);
04994 }
04995 } else if (s)
04996 astman_append(s, " No Callers%s", term);
04997 else
04998 ast_cli(fd, " No Callers%s", term);
04999 if (s)
05000 astman_append(s, "%s", term);
05001 else
05002 ast_cli(fd, "%s", term);
05003 ao2_unlock(q);
05004 if (queue_show)
05005 break;
05006 }
05007 AST_LIST_UNLOCK(&queues);
05008 return RESULT_SUCCESS;
05009 }
05010
05011 static int queue_show(int fd, int argc, char **argv)
05012 {
05013 return __queues_show(NULL, 0, fd, argc, argv);
05014 }
05015
05016 static char *complete_queue(const char *line, const char *word, int pos, int state)
05017 {
05018 struct call_queue *q;
05019 char *ret = NULL;
05020 int which = 0;
05021 int wordlen = strlen(word);
05022
05023 AST_LIST_LOCK(&queues);
05024 AST_LIST_TRAVERSE(&queues, q, list) {
05025 if (!strncasecmp(word, q->name, wordlen) && ++which > state) {
05026 ret = ast_strdup(q->name);
05027 break;
05028 }
05029 }
05030 AST_LIST_UNLOCK(&queues);
05031
05032 return ret;
05033 }
05034
05035 static char *complete_queue_show(const char *line, const char *word, int pos, int state)
05036 {
05037 if (pos == 2)
05038 return complete_queue(line, word, pos, state);
05039 return NULL;
05040 }
05041
05042
05043
05044
05045 static int manager_queues_show(struct mansession *s, const struct message *m)
05046 {
05047 char *a[] = { "queue", "show" };
05048
05049 __queues_show(s, 1, -1, 2, a);
05050 astman_append(s, "\r\n\r\n");
05051
05052 return RESULT_SUCCESS;
05053 }
05054
05055
05056 static int manager_queues_status(struct mansession *s, const struct message *m)
05057 {
05058 time_t now;
05059 int pos;
05060 const char *id = astman_get_header(m,"ActionID");
05061 const char *queuefilter = astman_get_header(m,"Queue");
05062 const char *memberfilter = astman_get_header(m,"Member");
05063 char idText[256] = "";
05064 struct call_queue *q;
05065 struct queue_ent *qe;
05066 float sl = 0;
05067 struct member *mem;
05068 struct ao2_iterator mem_iter;
05069
05070 astman_send_ack(s, m, "Queue status will follow");
05071 time(&now);
05072 AST_LIST_LOCK(&queues);
05073 if (!ast_strlen_zero(id))
05074 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
05075
05076 AST_LIST_TRAVERSE(&queues, q, list) {
05077 ao2_lock(q);
05078
05079
05080 if (ast_strlen_zero(queuefilter) || !strcmp(q->name, queuefilter)) {
05081 sl = ((q->callscompleted > 0) ? 100 * ((float)q->callscompletedinsl / (float)q->callscompleted) : 0);
05082 astman_append(s, "Event: QueueParams\r\n"
05083 "Queue: %s\r\n"
05084 "Max: %d\r\n"
05085 "Calls: %d\r\n"
05086 "Holdtime: %d\r\n"
05087 "Completed: %d\r\n"
05088 "Abandoned: %d\r\n"
05089 "ServiceLevel: %d\r\n"
05090 "ServicelevelPerf: %2.1f\r\n"
05091 "RingLimit: %d\r\n"
05092 "Weight: %d\r\n"
05093 "%s"
05094 "\r\n",
05095 q->name, q->maxlen, q->count, q->holdtime, q->callscompleted,
05096 q->callsabandoned, q->servicelevel, sl, q->ringlimit, q->weight, idText);
05097
05098 mem_iter = ao2_iterator_init(q->members, 0);
05099 while ((mem = ao2_iterator_next(&mem_iter))) {
05100 if (ast_strlen_zero(memberfilter) || !strcmp(mem->interface, memberfilter)) {
05101 astman_append(s, "Event: QueueMember\r\n"
05102 "Queue: %s\r\n"
05103 "Name: %s\r\n"
05104 "Location: %s\r\n"
05105 "Membership: %s\r\n"
05106 "Penalty: %d\r\n"
05107 "CallsTaken: %d\r\n"
05108 "LastCall: %d\r\n"
05109 "Status: %d\r\n"
05110 "Paused: %d\r\n"
05111 "%s"
05112 "\r\n",
05113 q->name, mem->membername, mem->interface, mem->dynamic ? "dynamic" : "static",
05114 mem->penalty, mem->calls, (int)mem->lastcall, mem->status, mem->paused, idText);
05115 }
05116 ao2_ref(mem, -1);
05117 }
05118 ao2_iterator_destroy(&mem_iter);
05119
05120 pos = 1;
05121 for (qe = q->head; qe; qe = qe->next) {
05122 astman_append(s, "Event: QueueEntry\r\n"
05123 "Queue: %s\r\n"
05124 "Position: %d\r\n"
05125 "Channel: %s\r\n"
05126 "CallerID: %s\r\n"
05127 "CallerIDName: %s\r\n"
05128 "Wait: %ld\r\n"
05129 "%s"
05130 "\r\n",
05131 q->name, pos++, qe->chan->name,
05132 S_OR(qe->chan->cid.cid_num, "unknown"),
05133 S_OR(qe->chan->cid.cid_name, "unknown"),
05134 (long) (now - qe->start), idText);
05135 }
05136 }
05137 ao2_unlock(q);
05138 }
05139
05140 astman_append(s,
05141 "Event: QueueStatusComplete\r\n"
05142 "%s"
05143 "\r\n",idText);
05144
05145 AST_LIST_UNLOCK(&queues);
05146
05147
05148 return RESULT_SUCCESS;
05149 }
05150
05151 static int manager_add_queue_member(struct mansession *s, const struct message *m)
05152 {
05153 const char *queuename, *interface, *penalty_s, *paused_s, *membername, *state_interface;
05154 int paused, penalty = 0;
05155
05156 queuename = astman_get_header(m, "Queue");
05157 interface = astman_get_header(m, "Interface");
05158 penalty_s = astman_get_header(m, "Penalty");
05159 paused_s = astman_get_header(m, "Paused");
05160 membername = astman_get_header(m, "MemberName");
05161 state_interface = astman_get_header(m, "StateInterface");
05162
05163 if (ast_strlen_zero(queuename)) {
05164 astman_send_error(s, m, "'Queue' not specified.");
05165 return 0;
05166 }
05167
05168 if (ast_strlen_zero(interface)) {
05169 astman_send_error(s, m, "'Interface' not specified.");
05170 return 0;
05171 }
05172
05173 if (ast_strlen_zero(penalty_s))
05174 penalty = 0;
05175 else if (sscanf(penalty_s, "%30d", &penalty) != 1 || penalty < 0)
05176 penalty = 0;
05177
05178 if (ast_strlen_zero(paused_s))
05179 paused = 0;
05180 else
05181 paused = abs(ast_true(paused_s));
05182
05183 switch (add_to_queue(queuename, interface, membername, penalty, paused, queue_persistent_members, state_interface)) {
05184 case RES_OKAY:
05185 ast_queue_log(queuename, "MANAGER", interface, "ADDMEMBER", "%s", "");
05186 astman_send_ack(s, m, "Added interface to queue");
05187 break;
05188 case RES_EXISTS:
05189 astman_send_error(s, m, "Unable to add interface: Already there");
05190 break;
05191 case RES_NOSUCHQUEUE:
05192 astman_send_error(s, m, "Unable to add interface to queue: No such queue");
05193 break;
05194 case RES_OUTOFMEMORY:
05195 astman_send_error(s, m, "Out of memory");
05196 break;
05197 }
05198
05199 return 0;
05200 }
05201
05202 static int manager_remove_queue_member(struct mansession *s, const struct message *m)
05203 {
05204 const char *queuename, *interface;
05205
05206 queuename = astman_get_header(m, "Queue");
05207 interface = astman_get_header(m, "Interface");
05208
05209 if (ast_strlen_zero(queuename) || ast_strlen_zero(interface)) {
05210 astman_send_error(s, m, "Need 'Queue' and 'Interface' parameters.");
05211 return 0;
05212 }
05213
05214 switch (remove_from_queue(queuename, interface)) {
05215 case RES_OKAY:
05216 ast_queue_log(queuename, "MANAGER", interface, "REMOVEMEMBER", "%s", "");
05217 astman_send_ack(s, m, "Removed interface from queue");
05218 break;
05219 case RES_EXISTS:
05220 astman_send_error(s, m, "Unable to remove interface: Not there");
05221 break;
05222 case RES_NOSUCHQUEUE:
05223 astman_send_error(s, m, "Unable to remove interface from queue: No such queue");
05224 break;
05225 case RES_OUTOFMEMORY:
05226 astman_send_error(s, m, "Out of memory");
05227 break;
05228 case RES_NOT_DYNAMIC:
05229 astman_send_error(s, m, "Member not dynamic");
05230 break;
05231 }
05232
05233 return 0;
05234 }
05235
05236 static int manager_pause_queue_member(struct mansession *s, const struct message *m)
05237 {
05238 const char *queuename, *interface, *paused_s;
05239 int paused;
05240
05241 interface = astman_get_header(m, "Interface");
05242 paused_s = astman_get_header(m, "Paused");
05243 queuename = astman_get_header(m, "Queue");
05244
05245 if (ast_strlen_zero(interface) || ast_strlen_zero(paused_s)) {
05246 astman_send_error(s, m, "Need 'Interface' and 'Paused' parameters.");
05247 return 0;
05248 }
05249
05250 paused = abs(ast_true(paused_s));
05251
05252 if (set_member_paused(queuename, interface, paused))
05253 astman_send_error(s, m, "Interface not found");
05254 else
05255 astman_send_ack(s, m, paused ? "Interface paused successfully" : "Interface unpaused successfully");
05256 return 0;
05257 }
05258
05259 static int handle_queue_add_member(int fd, int argc, char *argv[])
05260 {
05261 char *queuename, *interface, *membername = NULL, *state_interface = NULL;
05262 int penalty;
05263
05264 if ((argc != 6) && (argc != 8) && (argc != 10) && (argc != 12)) {
05265 return RESULT_SHOWUSAGE;
05266 } else if (strcmp(argv[4], "to")) {
05267 return RESULT_SHOWUSAGE;
05268 } else if ((argc == 8) && strcmp(argv[6], "penalty")) {
05269 return RESULT_SHOWUSAGE;
05270 } else if ((argc == 10) && strcmp(argv[8], "as")) {
05271 return RESULT_SHOWUSAGE;
05272 } else if ((argc == 12) && strcmp(argv[10], "state_interface")) {
05273 return RESULT_SHOWUSAGE;
05274 }
05275
05276 queuename = argv[5];
05277 interface = argv[3];
05278 if (argc >= 8) {
05279 if (sscanf(argv[7], "%30d", &penalty) == 1) {
05280 if (penalty < 0) {
05281 ast_cli(fd, "Penalty must be >= 0\n");
05282 penalty = 0;
05283 }
05284 } else {
05285 ast_cli(fd, "Penalty must be an integer >= 0\n");
05286 penalty = 0;
05287 }
05288 } else {
05289 penalty = 0;
05290 }
05291
05292 if (argc >= 10) {
05293 membername = argv[9];
05294 }
05295
05296 if (argc >= 12) {
05297 state_interface = argv[11];
05298 }
05299
05300 switch (add_to_queue(queuename, interface, membername, penalty, 0, queue_persistent_members, state_interface)) {
05301 case RES_OKAY:
05302 ast_queue_log(queuename, "CLI", interface, "ADDMEMBER", "%s", "");
05303 ast_cli(fd, "Added interface '%s' to queue '%s'\n", interface, queuename);
05304 return RESULT_SUCCESS;
05305 case RES_EXISTS:
05306 ast_cli(fd, "Unable to add interface '%s' to queue '%s': Already there\n", interface, queuename);
05307 return RESULT_FAILURE;
05308 case RES_NOSUCHQUEUE:
05309 ast_cli(fd, "Unable to add interface to queue '%s': No such queue\n", queuename);
05310 return RESULT_FAILURE;
05311 case RES_OUTOFMEMORY:
05312 ast_cli(fd, "Out of memory\n");
05313 return RESULT_FAILURE;
05314 default:
05315 return RESULT_FAILURE;
05316 }
05317 }
05318
05319 static char *complete_queue_add_member(const char *line, const char *word, int pos, int state)
05320 {
05321
05322 switch (pos) {
05323 case 3:
05324 return NULL;
05325 case 4:
05326 return state == 0 ? ast_strdup("to") : NULL;
05327 case 5:
05328 return complete_queue(line, word, pos, state);
05329 case 6:
05330 return state == 0 ? ast_strdup("penalty") : NULL;
05331 case 7:
05332 if (state < 100) {
05333 char *num;
05334 if ((num = ast_malloc(3))) {
05335 sprintf(num, "%d", state);
05336 }
05337 return num;
05338 } else {
05339 return NULL;
05340 }
05341 case 8:
05342 return state == 0 ? ast_strdup("as") : NULL;
05343 case 9:
05344 return NULL;
05345 case 10:
05346 return state == 0 ? ast_strdup("state_interface") : NULL;
05347 default:
05348 return NULL;
05349 }
05350 }
05351
05352 static int handle_queue_remove_member(int fd, int argc, char *argv[])
05353 {
05354 char *queuename, *interface;
05355
05356 if (argc != 6) {
05357 return RESULT_SHOWUSAGE;
05358 } else if (strcmp(argv[4], "from")) {
05359 return RESULT_SHOWUSAGE;
05360 }
05361
05362 queuename = argv[5];
05363 interface = argv[3];
05364
05365 switch (remove_from_queue(queuename, interface)) {
05366 case RES_OKAY:
05367 ast_queue_log(queuename, "CLI", interface, "REMOVEMEMBER", "%s", "");
05368 ast_cli(fd, "Removed interface '%s' from queue '%s'\n", interface, queuename);
05369 return RESULT_SUCCESS;
05370 case RES_EXISTS:
05371 ast_cli(fd, "Unable to remove interface '%s' from queue '%s': Not there\n", interface, queuename);
05372 return RESULT_FAILURE;
05373 case RES_NOSUCHQUEUE:
05374 ast_cli(fd, "Unable to remove interface from queue '%s': No such queue\n", queuename);
05375 return RESULT_FAILURE;
05376 case RES_OUTOFMEMORY:
05377 ast_cli(fd, "Out of memory\n");
05378 return RESULT_FAILURE;
05379 case RES_NOT_DYNAMIC:
05380 ast_cli(fd, "Member not dynamic\n");
05381 return RESULT_FAILURE;
05382 default:
05383 return RESULT_FAILURE;
05384 }
05385 }
05386
05387 static char *complete_queue_remove_member(const char *line, const char *word, int pos, int state)
05388 {
05389 int which = 0;
05390 struct call_queue *q;
05391 struct member *m;
05392 struct ao2_iterator mem_iter;
05393
05394
05395 if (pos > 5 || pos < 3)
05396 return NULL;
05397 if (pos == 4)
05398 return state == 0 ? ast_strdup("from") : NULL;
05399
05400 if (pos == 5)
05401 return complete_queue(line, word, pos, state);
05402
05403
05404 if (!AST_LIST_EMPTY(&queues)) {
05405 AST_LIST_TRAVERSE(&queues, q, list) {
05406 ao2_lock(q);
05407 mem_iter = ao2_iterator_init(q->members, 0);
05408 while ((m = ao2_iterator_next(&mem_iter))) {
05409 if (++which > state) {
05410 char *tmp;
05411 ao2_iterator_destroy(&mem_iter);
05412 ao2_unlock(q);
05413 tmp = ast_strdup(m->interface);
05414 ao2_ref(m, -1);
05415 return tmp;
05416 }
05417 ao2_ref(m, -1);
05418 }
05419 ao2_iterator_destroy(&mem_iter);
05420 ao2_unlock(q);
05421 }
05422 }
05423
05424 return NULL;
05425 }
05426
05427
05428
05429
05430 struct member_count {
05431 int paused;
05432 int inuse;
05433 int valid;
05434 int active;
05435 int free;
05436 int all;
05437 };
05438
05439 static int queue_member_count(const char *qname, struct member_count *qmc)
05440 {
05441 int res = 0;
05442 struct call_queue *q;
05443 struct member *m;
05444 struct ao2_iterator mem_iter;
05445
05446 if ((q = load_realtime_queue(qname))) {
05447 ao2_lock(q);
05448 mem_iter = ao2_iterator_init(q->members, 0);
05449 while ((m = ao2_iterator_next(&mem_iter))) {
05450
05451 if (m->status == AST_DEVICE_INUSE) {
05452 qmc->inuse++;
05453 }
05454
05455 if ((m->status != AST_DEVICE_UNAVAILABLE) && (m->status != AST_DEVICE_INVALID)) {
05456 qmc->valid++;
05457 }
05458
05459 if (m->paused) {
05460 qmc->paused++;
05461 }
05462
05463 if (!m->paused && (m->status != AST_DEVICE_UNAVAILABLE) && (m->status != AST_DEVICE_INVALID)) {
05464 qmc->active++;
05465 }
05466
05467 if (!m->paused && ((m->status == AST_DEVICE_UNKNOWN) || (m->status == AST_DEVICE_NOT_INUSE))) {
05468 qmc->free++;
05469 }
05470 qmc->all++;
05471 ao2_ref(m, -1);
05472 }
05473 ao2_unlock(q);
05474 } else {
05475 ast_log(LOG_WARNING, "Queue %s was not found\n", qname);
05476 res = -1;
05477 }
05478 return res;
05479 }
05480
05481 static int qmc_handler(const char *queuename, char *buffer, int len)
05482 {
05483 struct member_count qmc;
05484 memset(&qmc, 0, sizeof(qmc));
05485 if (queue_member_count(queuename, &qmc) != 0) {
05486 return RESULT_FAILURE;
05487 } else {
05488 snprintf(buffer, len,
05489 "valid:%d inuse:%d paused:%d active:%d free:%d all:%d",
05490 qmc.valid, qmc.inuse, qmc.paused, qmc.active, qmc.free, qmc.all);
05491 return RESULT_SUCCESS;
05492 }
05493 }
05494
05495
05496 static int manager_queue_member_count(struct mansession *s, const struct message *m)
05497 {
05498 char buffer[256] = "";
05499 const char *queuename = astman_get_header(m,"Queue");
05500
05501 if (ast_strlen_zero(queuename)) {
05502 astman_send_error(s, m, "'Queue' not specified.");
05503 return 0;
05504 }
05505 if (qmc_handler(queuename, buffer, sizeof(buffer)) == RESULT_SUCCESS) {
05506 astman_send_ack(s, m, buffer);
05507 return RESULT_SUCCESS;
05508 } else {
05509 astman_send_error(s, m, "Queue not found.");
05510 return 0;
05511 }
05512 }
05513
05514 static int cli_queue_member_count(int fd, int argc, char **argv)
05515 {
05516 char buffer[256] = "";
05517 char *queuename;
05518
05519 if (argc != 4) {
05520 return RESULT_SHOWUSAGE;
05521 }
05522 queuename = argv[3];
05523
05524 if (qmc_handler(queuename, buffer, sizeof(buffer)) == RESULT_SUCCESS) {
05525 ast_cli(fd,
05526 "Member count for queue '%s'\n"
05527 "%s\n",
05528 queuename, buffer);
05529 return RESULT_SUCCESS;
05530 } else {
05531 ast_cli(fd, "No such queue: '%s'\n", queuename);
05532 return RESULT_FAILURE;
05533 }
05534 }
05535
05536 static char qmc_cmd_usage[] =
05537 "Usage: queue member count <queue>\n"
05538 " Provides member count information on a specified queue.\n";
05539
05540
05541 static char *complete_queue_member_count(const char *line, const char *word, int pos, int state)
05542 {
05543
05544 switch (pos) {
05545 case 3:
05546 return complete_queue(line, word, pos, state);
05547 default:
05548 return NULL;
05549 }
05550 }
05551
05552
05553
05554
05555 static char queue_show_usage[] =
05556 "Usage: queue show\n"
05557 " Provides summary information on a specified queue.\n";
05558
05559 static char qam_cmd_usage[] =
05560 "Usage: queue add member <channel> to <queue> [penalty <penalty> [as <membername> [state_interface <state_interface>]]]\n";
05561
05562 static char qrm_cmd_usage[] =
05563 "Usage: queue remove member <channel> from <queue>\n";
05564
05565 static struct ast_cli_entry cli_show_queue_deprecated = {
05566 { "show", "queue", NULL },
05567 queue_show, NULL,
05568 NULL, complete_queue_show };
05569
05570 static struct ast_cli_entry cli_add_queue_member_deprecated = {
05571 { "add", "queue", "member", NULL },
05572 handle_queue_add_member, NULL,
05573 NULL, complete_queue_add_member };
05574
05575 static struct ast_cli_entry cli_remove_queue_member_deprecated = {
05576 { "remove", "queue", "member", NULL },
05577 handle_queue_remove_member, NULL,
05578 NULL, complete_queue_remove_member };
05579
05580 static struct ast_cli_entry cli_queue[] = {
05581
05582 { { "show", "queues", NULL },
05583 queue_show, NULL,
05584 NULL, NULL },
05585
05586 { { "queue", "show", NULL },
05587 queue_show, "Show status of a specified queue",
05588 queue_show_usage, complete_queue_show, &cli_show_queue_deprecated },
05589
05590 { { "queue", "add", "member", NULL },
05591 handle_queue_add_member, "Add a channel to a specified queue",
05592 qam_cmd_usage, complete_queue_add_member, &cli_add_queue_member_deprecated },
05593
05594 { { "queue", "remove", "member", NULL },
05595 handle_queue_remove_member, "Removes a channel from a specified queue",
05596 qrm_cmd_usage, complete_queue_remove_member, &cli_remove_queue_member_deprecated },
05597
05598 { { "queue", "member", "count", NULL },
05599 cli_queue_member_count, "Show queue member count information",
05600 qmc_cmd_usage, complete_queue_member_count, NULL },
05601
05602 };
05603
05604 static int unload_module(void)
05605 {
05606 int res;
05607
05608 if (device_state.thread != AST_PTHREADT_NULL) {
05609 device_state.stop = 1;
05610 ast_mutex_lock(&device_state.lock);
05611 ast_cond_signal(&device_state.cond);
05612 ast_mutex_unlock(&device_state.lock);
05613 pthread_join(device_state.thread, NULL);
05614 }
05615
05616 ast_cli_unregister_multiple(cli_queue, sizeof(cli_queue) / sizeof(struct ast_cli_entry));
05617 res = ast_manager_unregister("QueueStatus");
05618 res |= ast_manager_unregister("Queues");
05619 res |= ast_manager_unregister("QueueAdd");
05620 res |= ast_manager_unregister("QueueRemove");
05621 res |= ast_manager_unregister("QueuePause");
05622 res |= ast_unregister_application(app_aqm);
05623 res |= ast_unregister_application(app_rqm);
05624 res |= ast_unregister_application(app_pqm);
05625 res |= ast_unregister_application(app_upqm);
05626 res |= ast_unregister_application(app_ql);
05627 res |= ast_unregister_application(app);
05628 res |= ast_custom_function_unregister(&queueagentcount_function);
05629 res |= ast_custom_function_unregister(&queuemembercount_function);
05630 res |= ast_custom_function_unregister(&queuememberlist_function);
05631 res |= ast_custom_function_unregister(&queuewaitingcount_function);
05632 ast_devstate_del(statechange_queue, NULL);
05633
05634 ast_module_user_hangup_all();
05635
05636 clear_and_free_interfaces();
05637
05638 return res;
05639 }
05640
05641 static int load_module(void)
05642 {
05643 int res;
05644
05645 if (!reload_queues())
05646 return AST_MODULE_LOAD_DECLINE;
05647
05648 if (queue_persistent_members)
05649 reload_queue_members();
05650
05651 ast_mutex_init(&device_state.lock);
05652 ast_cond_init(&device_state.cond, NULL);
05653 ast_pthread_create(&device_state.thread, NULL, device_state_thread, NULL);
05654
05655 ast_cli_register_multiple(cli_queue, sizeof(cli_queue) / sizeof(struct ast_cli_entry));
05656 res = ast_register_application(app, queue_exec, synopsis, descrip);
05657 res |= ast_register_application(app_aqm, aqm_exec, app_aqm_synopsis, app_aqm_descrip);
05658 res |= ast_register_application(app_rqm, rqm_exec, app_rqm_synopsis, app_rqm_descrip);
05659 res |= ast_register_application(app_pqm, pqm_exec, app_pqm_synopsis, app_pqm_descrip);
05660 res |= ast_register_application(app_upqm, upqm_exec, app_upqm_synopsis, app_upqm_descrip);
05661 res |= ast_register_application(app_ql, ql_exec, app_ql_synopsis, app_ql_descrip);
05662 res |= ast_manager_register("Queues", 0, manager_queues_show, "Queues");
05663 res |= ast_manager_register("QueueStatus", 0, manager_queues_status, "Queue Status");
05664 res |= ast_manager_register("QueueMemberCount", 0, manager_queue_member_count, "Queue Member Count");
05665 res |= ast_manager_register("QueueAdd", EVENT_FLAG_AGENT, manager_add_queue_member, "Add interface to queue.");
05666 res |= ast_manager_register("QueueRemove", EVENT_FLAG_AGENT, manager_remove_queue_member, "Remove interface from queue.");
05667 res |= ast_manager_register("QueuePause", EVENT_FLAG_AGENT, manager_pause_queue_member, "Makes a queue member temporarily unavailable");
05668 res |= ast_custom_function_register(&queueagentcount_function);
05669 res |= ast_custom_function_register(&queuemembercount_function);
05670 res |= ast_custom_function_register(&queuememberlist_function);
05671 res |= ast_custom_function_register(&queuewaitingcount_function);
05672 res |= ast_devstate_add(statechange_queue, NULL);
05673
05674 return res;
05675 }
05676
05677 static int reload(void)
05678 {
05679 reload_queues();
05680 return 0;
05681 }
05682
05683 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "True Call Queueing",
05684 .load = load_module,
05685 .unload = unload_module,
05686 .reload = reload,
05687 );
05688