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