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