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