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