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