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