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