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