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: 152538 $")
00066
00067 #include <stdlib.h>
00068 #include <errno.h>
00069 #include <unistd.h>
00070 #include <string.h>
00071 #include <stdlib.h>
00072 #include <stdio.h>
00073 #include <sys/time.h>
00074 #include <sys/signal.h>
00075 #include <netinet/in.h>
00076
00077 #include "asterisk/lock.h"
00078 #include "asterisk/file.h"
00079 #include "asterisk/logger.h"
00080 #include "asterisk/channel.h"
00081 #include "asterisk/pbx.h"
00082 #include "asterisk/options.h"
00083 #include "asterisk/app.h"
00084 #include "asterisk/linkedlists.h"
00085 #include "asterisk/module.h"
00086 #include "asterisk/translate.h"
00087 #include "asterisk/say.h"
00088 #include "asterisk/features.h"
00089 #include "asterisk/musiconhold.h"
00090 #include "asterisk/cli.h"
00091 #include "asterisk/manager.h"
00092 #include "asterisk/config.h"
00093 #include "asterisk/monitor.h"
00094 #include "asterisk/utils.h"
00095 #include "asterisk/causes.h"
00096 #include "asterisk/astdb.h"
00097 #include "asterisk/devicestate.h"
00098 #include "asterisk/stringfields.h"
00099 #include "asterisk/astobj2.h"
00100 #include "asterisk/global_datastores.h"
00101
00102
00103
00104
00105
00106
00107
00108
00109
00110
00111
00112
00113
00114
00115
00116 enum {
00117 QUEUE_STRATEGY_RINGALL = 0,
00118 QUEUE_STRATEGY_ROUNDROBIN,
00119 QUEUE_STRATEGY_LEASTRECENT,
00120 QUEUE_STRATEGY_FEWESTCALLS,
00121 QUEUE_STRATEGY_RANDOM,
00122 QUEUE_STRATEGY_RRMEMORY
00123 };
00124
00125 static struct strategy {
00126 int strategy;
00127 char *name;
00128 } strategies[] = {
00129 { QUEUE_STRATEGY_RINGALL, "ringall" },
00130 { QUEUE_STRATEGY_ROUNDROBIN, "roundrobin" },
00131 { QUEUE_STRATEGY_LEASTRECENT, "leastrecent" },
00132 { QUEUE_STRATEGY_FEWESTCALLS, "fewestcalls" },
00133 { QUEUE_STRATEGY_RANDOM, "random" },
00134 { QUEUE_STRATEGY_RRMEMORY, "rrmemory" },
00135 };
00136
00137 #define DEFAULT_RETRY 5
00138 #define DEFAULT_TIMEOUT 15
00139 #define RECHECK 1
00140 #define MAX_PERIODIC_ANNOUNCEMENTS 10
00141
00142 #define RES_OKAY 0
00143 #define RES_EXISTS (-1)
00144 #define RES_OUTOFMEMORY (-2)
00145 #define RES_NOSUCHQUEUE (-3)
00146 #define RES_NOT_DYNAMIC (-4)
00147
00148 static char *app = "Queue";
00149
00150 static char *synopsis = "Queue a call for a call queue";
00151
00152 static char *descrip =
00153 " Queue(queuename[|options[|URL][|announceoverride][|timeout][|AGI]):\n"
00154 "Queues an incoming call in a particular call queue as defined in queues.conf.\n"
00155 "This application will return to the dialplan if the queue does not exist, or\n"
00156 "any of the join options cause the caller to not enter the queue.\n"
00157 "The option string may contain zero or more of the following characters:\n"
00158 " 'd' -- data-quality (modem) call (minimum delay).\n"
00159 " 'h' -- allow callee to hang up by hitting '*', or whatver disconnect sequence\n"
00160 " defined in the featuremap section in features.conf.\n"
00161 " 'H' -- allow caller to hang up by hitting '*', or whatever disconnect sequence\n"
00162 " defined in the featuremap section in features.conf.\n"
00163 " 'n' -- no retries on the timeout; will exit this application and \n"
00164 " go to the next step.\n"
00165 " 'i' -- ignore call forward requests from queue members and do nothing\n"
00166 " when they are requested.\n"
00167 " 'r' -- ring instead of playing MOH\n"
00168 " 't' -- allow the called user transfer the calling user by pressing '#' or\n"
00169 " whatever blindxfer sequence defined in the featuremap section in\n"
00170 " features.conf\n"
00171 " 'T' -- to allow the calling user to transfer the call by pressing '#' or\n"
00172 " whatever blindxfer sequence defined in the featuremap section in\n"
00173 " features.conf\n"
00174 " 'w' -- allow the called user to write the conversation to disk via Monitor\n"
00175 " by pressing the automon sequence defined in the featuremap section in\n"
00176 " features.conf\n"
00177 " 'W' -- allow the calling user to write the conversation to disk via Monitor\n"
00178 " by pressing the automon sequence defined in the featuremap section in\n"
00179 " features.conf\n"
00180 " In addition to transferring the call, a call may be parked and then picked\n"
00181 "up by another user, by transferring to the parking lot extension. See features.conf.\n"
00182 " The optional URL will be sent to the called party if the channel supports\n"
00183 "it.\n"
00184 " The optional AGI parameter will setup an AGI script to be executed on the \n"
00185 "calling party's channel once they are connected to a queue member.\n"
00186 " The timeout will cause the queue to fail out after a specified number of\n"
00187 "seconds, checked between each queues.conf 'timeout' and 'retry' cycle.\n"
00188 " This application sets the following channel variable upon completion:\n"
00189 " QUEUESTATUS The status of the call as a text string, one of\n"
00190 " TIMEOUT | FULL | JOINEMPTY | LEAVEEMPTY | JOINUNAVAIL | LEAVEUNAVAIL\n";
00191
00192 static char *app_aqm = "AddQueueMember" ;
00193 static char *app_aqm_synopsis = "Dynamically adds queue members" ;
00194 static char *app_aqm_descrip =
00195 " AddQueueMember(queuename[|interface[|penalty[|options[|membername]]]]):\n"
00196 "Dynamically adds interface to an existing queue.\n"
00197 "If the interface is already in the queue and there exists an n+101 priority\n"
00198 "then it will then jump to this priority. Otherwise it will return an error\n"
00199 "The option string may contain zero or more of the following characters:\n"
00200 " 'j' -- jump to +101 priority when appropriate.\n"
00201 " This application sets the following channel variable upon completion:\n"
00202 " AQMSTATUS The status of the attempt to add a queue member as a \n"
00203 " text string, one of\n"
00204 " ADDED | MEMBERALREADY | NOSUCHQUEUE \n"
00205 "Example: AddQueueMember(techsupport|SIP/3000)\n"
00206 "";
00207
00208 static char *app_rqm = "RemoveQueueMember" ;
00209 static char *app_rqm_synopsis = "Dynamically removes queue members" ;
00210 static char *app_rqm_descrip =
00211 " RemoveQueueMember(queuename[|interface[|options]]):\n"
00212 "Dynamically removes interface to an existing queue\n"
00213 "If the interface is NOT in the queue and there exists an n+101 priority\n"
00214 "then it will then jump to this priority. Otherwise it will return an error\n"
00215 "The option string may contain zero or more of the following characters:\n"
00216 " 'j' -- jump to +101 priority when appropriate.\n"
00217 " This application sets the following channel variable upon completion:\n"
00218 " RQMSTATUS The status of the attempt to remove a queue member as a\n"
00219 " text string, one of\n"
00220 " REMOVED | NOTINQUEUE | NOSUCHQUEUE \n"
00221 "Example: RemoveQueueMember(techsupport|SIP/3000)\n"
00222 "";
00223
00224 static char *app_pqm = "PauseQueueMember" ;
00225 static char *app_pqm_synopsis = "Pauses a queue member" ;
00226 static char *app_pqm_descrip =
00227 " PauseQueueMember([queuename]|interface[|options]):\n"
00228 "Pauses (blocks calls for) a queue member.\n"
00229 "The given interface will be paused in the given queue. This prevents\n"
00230 "any calls from being sent from the queue to the interface until it is\n"
00231 "unpaused with UnpauseQueueMember or the manager interface. If no\n"
00232 "queuename is given, the interface is paused in every queue it is a\n"
00233 "member of. If the interface is not in the named queue, or if no queue\n"
00234 "is given and the interface is not in any queue, it will jump to\n"
00235 "priority n+101, if it exists and the appropriate options are set.\n"
00236 "The application will fail if the interface is not found and no extension\n"
00237 "to jump to exists.\n"
00238 "The option string may contain zero or more of the following characters:\n"
00239 " 'j' -- jump to +101 priority when appropriate.\n"
00240 " This application sets the following channel variable upon completion:\n"
00241 " PQMSTATUS The status of the attempt to pause a queue member as a\n"
00242 " text string, one of\n"
00243 " PAUSED | NOTFOUND\n"
00244 "Example: PauseQueueMember(|SIP/3000)\n";
00245
00246 static char *app_upqm = "UnpauseQueueMember" ;
00247 static char *app_upqm_synopsis = "Unpauses a queue member" ;
00248 static char *app_upqm_descrip =
00249 " UnpauseQueueMember([queuename]|interface[|options]):\n"
00250 "Unpauses (resumes calls to) a queue member.\n"
00251 "This is the counterpart to PauseQueueMember and operates exactly the\n"
00252 "same way, except it unpauses instead of pausing the given interface.\n"
00253 "The option string may contain zero or more of the following characters:\n"
00254 " 'j' -- jump to +101 priority when appropriate.\n"
00255 " This application sets the following channel variable upon completion:\n"
00256 " UPQMSTATUS The status of the attempt to unpause a queue \n"
00257 " member as a text string, one of\n"
00258 " UNPAUSED | NOTFOUND\n"
00259 "Example: UnpauseQueueMember(|SIP/3000)\n";
00260
00261 static char *app_ql = "QueueLog" ;
00262 static char *app_ql_synopsis = "Writes to the queue_log" ;
00263 static char *app_ql_descrip =
00264 " QueueLog(queuename|uniqueid|agent|event[|additionalinfo]):\n"
00265 "Allows you to write your own events into the queue log\n"
00266 "Example: QueueLog(101|${UNIQUEID}|${AGENT}|WENTONBREAK|600)\n";
00267
00268
00269 static const char *pm_family = "Queue/PersistentMembers";
00270
00271 #define PM_MAX_LEN 8192
00272
00273
00274 static int queue_persistent_members = 0;
00275
00276
00277 static int use_weight = 0;
00278
00279
00280 static int autofill_default = 0;
00281
00282
00283 static int montype_default = 0;
00284
00285 enum queue_result {
00286 QUEUE_UNKNOWN = 0,
00287 QUEUE_TIMEOUT = 1,
00288 QUEUE_JOINEMPTY = 2,
00289 QUEUE_LEAVEEMPTY = 3,
00290 QUEUE_JOINUNAVAIL = 4,
00291 QUEUE_LEAVEUNAVAIL = 5,
00292 QUEUE_FULL = 6,
00293 };
00294
00295 const struct {
00296 enum queue_result id;
00297 char *text;
00298 } queue_results[] = {
00299 { QUEUE_UNKNOWN, "UNKNOWN" },
00300 { QUEUE_TIMEOUT, "TIMEOUT" },
00301 { QUEUE_JOINEMPTY,"JOINEMPTY" },
00302 { QUEUE_LEAVEEMPTY, "LEAVEEMPTY" },
00303 { QUEUE_JOINUNAVAIL, "JOINUNAVAIL" },
00304 { QUEUE_LEAVEUNAVAIL, "LEAVEUNAVAIL" },
00305 { QUEUE_FULL, "FULL" },
00306 };
00307
00308
00309
00310
00311
00312
00313
00314
00315
00316
00317
00318
00319 struct callattempt {
00320 struct callattempt *q_next;
00321 struct callattempt *call_next;
00322 struct ast_channel *chan;
00323 char interface[256];
00324 int stillgoing;
00325 int metric;
00326 int oldstatus;
00327 time_t lastcall;
00328 struct member *member;
00329 };
00330
00331
00332 struct queue_ent {
00333 struct call_queue *parent;
00334 char moh[80];
00335 char announce[80];
00336 char context[AST_MAX_CONTEXT];
00337 char digits[AST_MAX_EXTENSION];
00338 int valid_digits;
00339 int pos;
00340 int prio;
00341 int last_pos_said;
00342 time_t last_periodic_announce_time;
00343 int last_periodic_announce_sound;
00344 time_t last_pos;
00345 int opos;
00346 int handled;
00347 int pending;
00348 int max_penalty;
00349 time_t start;
00350 time_t expire;
00351 struct ast_channel *chan;
00352 struct queue_ent *next;
00353 };
00354
00355 struct member {
00356 char interface[80];
00357 char membername[80];
00358 int penalty;
00359 int calls;
00360 int dynamic;
00361 int realtime;
00362 int status;
00363 int paused;
00364 time_t lastcall;
00365 unsigned int dead:1;
00366 unsigned int delme:1;
00367 };
00368
00369 struct member_interface {
00370 char interface[80];
00371 AST_LIST_ENTRY(member_interface) list;
00372 };
00373
00374 static AST_LIST_HEAD_STATIC(interfaces, member_interface);
00375
00376
00377 #define QUEUE_EMPTY_NORMAL 1
00378 #define QUEUE_EMPTY_STRICT 2
00379 #define ANNOUNCEHOLDTIME_ALWAYS 1
00380 #define ANNOUNCEHOLDTIME_ONCE 2
00381 #define QUEUE_EVENT_VARIABLES 3
00382
00383 struct call_queue {
00384 ast_mutex_t lock;
00385 char name[80];
00386 char moh[80];
00387 char announce[80];
00388 char context[AST_MAX_CONTEXT];
00389 unsigned int monjoin:1;
00390 unsigned int dead:1;
00391 unsigned int joinempty:2;
00392 unsigned int eventwhencalled:2;
00393 unsigned int leavewhenempty:2;
00394 unsigned int ringinuse:1;
00395 unsigned int setinterfacevar:1;
00396 unsigned int reportholdtime:1;
00397 unsigned int wrapped:1;
00398 unsigned int timeoutrestart:1;
00399 unsigned int announceholdtime:2;
00400 int strategy:4;
00401 unsigned int maskmemberstatus:1;
00402 unsigned int realtime:1;
00403 unsigned int found:1;
00404 int announcefrequency;
00405 int periodicannouncefrequency;
00406 int roundingseconds;
00407 int holdtime;
00408 int callscompleted;
00409 int callsabandoned;
00410 int servicelevel;
00411 int callscompletedinsl;
00412 char monfmt[8];
00413 int montype;
00414 char sound_next[80];
00415 char sound_thereare[80];
00416 char sound_calls[80];
00417 char sound_holdtime[80];
00418 char sound_minutes[80];
00419 char sound_lessthan[80];
00420 char sound_seconds[80];
00421 char sound_thanks[80];
00422 char sound_reporthold[80];
00423 char sound_periodicannounce[MAX_PERIODIC_ANNOUNCEMENTS][80];
00424
00425 int count;
00426 int maxlen;
00427 int wrapuptime;
00428
00429 int retry;
00430 int timeout;
00431 int weight;
00432 int autopause;
00433
00434
00435 int rrpos;
00436 int memberdelay;
00437 int autofill;
00438
00439 struct ao2_container *members;
00440
00441
00442
00443
00444
00445 int membercount;
00446 struct queue_ent *head;
00447 AST_LIST_ENTRY(call_queue) list;
00448 };
00449
00450 static AST_LIST_HEAD_STATIC(queues, call_queue);
00451
00452 static int set_member_paused(const char *queuename, const char *interface, int paused);
00453
00454 static void queue_transfer_fixup(void *data, struct ast_channel *old_chan, struct ast_channel *new_chan);
00455
00456 static void rr_dep_warning(void)
00457 {
00458 static unsigned int warned = 0;
00459
00460 if (!warned) {
00461 ast_log(LOG_NOTICE, "The 'roundrobin' queue strategy is deprecated. Please use the 'rrmemory' strategy instead.\n");
00462 warned = 1;
00463 }
00464 }
00465
00466 static void monjoin_dep_warning(void)
00467 {
00468 static unsigned int warned = 0;
00469 if (!warned) {
00470 ast_log(LOG_NOTICE, "The 'monitor-join' queue option is deprecated. Please use monitor-type=mixmonitor instead.\n");
00471 warned = 1;
00472 }
00473 }
00474
00475 static void set_queue_result(struct ast_channel *chan, enum queue_result res)
00476 {
00477 int i;
00478
00479 for (i = 0; i < sizeof(queue_results) / sizeof(queue_results[0]); i++) {
00480 if (queue_results[i].id == res) {
00481 pbx_builtin_setvar_helper(chan, "QUEUESTATUS", queue_results[i].text);
00482 return;
00483 }
00484 }
00485 }
00486
00487 static char *int2strat(int strategy)
00488 {
00489 int x;
00490
00491 for (x = 0; x < sizeof(strategies) / sizeof(strategies[0]); x++) {
00492 if (strategy == strategies[x].strategy)
00493 return strategies[x].name;
00494 }
00495
00496 return "<unknown>";
00497 }
00498
00499 static int strat2int(const char *strategy)
00500 {
00501 int x;
00502
00503 for (x = 0; x < sizeof(strategies) / sizeof(strategies[0]); x++) {
00504 if (!strcasecmp(strategy, strategies[x].name))
00505 return strategies[x].strategy;
00506 }
00507
00508 return -1;
00509 }
00510
00511
00512 static inline void insert_entry(struct call_queue *q, struct queue_ent *prev, struct queue_ent *new, int *pos)
00513 {
00514 struct queue_ent *cur;
00515
00516 if (!q || !new)
00517 return;
00518 if (prev) {
00519 cur = prev->next;
00520 prev->next = new;
00521 } else {
00522 cur = q->head;
00523 q->head = new;
00524 }
00525 new->next = cur;
00526 new->parent = q;
00527 new->pos = ++(*pos);
00528 new->opos = *pos;
00529 }
00530
00531 enum queue_member_status {
00532 QUEUE_NO_MEMBERS,
00533 QUEUE_NO_REACHABLE_MEMBERS,
00534 QUEUE_NORMAL
00535 };
00536
00537
00538
00539
00540
00541
00542
00543 static enum queue_member_status get_member_status(struct call_queue *q, int max_penalty)
00544 {
00545 struct member *member;
00546 struct ao2_iterator mem_iter;
00547 enum queue_member_status result = QUEUE_NO_MEMBERS;
00548
00549 ast_mutex_lock(&q->lock);
00550 mem_iter = ao2_iterator_init(q->members, 0);
00551 while ((member = ao2_iterator_next(&mem_iter))) {
00552 if (max_penalty && (member->penalty > max_penalty)) {
00553 ao2_ref(member, -1);
00554 continue;
00555 }
00556
00557 if (member->paused) {
00558 ao2_ref(member, -1);
00559 continue;
00560 }
00561
00562 switch (member->status) {
00563 case AST_DEVICE_INVALID:
00564
00565 ao2_ref(member, -1);
00566 break;
00567 case AST_DEVICE_UNAVAILABLE:
00568 result = QUEUE_NO_REACHABLE_MEMBERS;
00569 ao2_ref(member, -1);
00570 break;
00571 default:
00572 ast_mutex_unlock(&q->lock);
00573 ao2_ref(member, -1);
00574 return QUEUE_NORMAL;
00575 }
00576 }
00577
00578 ast_mutex_unlock(&q->lock);
00579 return result;
00580 }
00581
00582 struct statechange {
00583 AST_LIST_ENTRY(statechange) entry;
00584 int state;
00585 char dev[0];
00586 };
00587
00588 static int update_status(const char *interface, const int status)
00589 {
00590 struct member *cur;
00591 struct ao2_iterator mem_iter;
00592 struct call_queue *q;
00593
00594 AST_LIST_LOCK(&queues);
00595 AST_LIST_TRAVERSE(&queues, q, list) {
00596 ast_mutex_lock(&q->lock);
00597 mem_iter = ao2_iterator_init(q->members, 0);
00598 while ((cur = ao2_iterator_next(&mem_iter))) {
00599 char *tmp_interface;
00600 char *slash_pos;
00601 tmp_interface = ast_strdupa(cur->interface);
00602 if ((slash_pos = strchr(tmp_interface, '/')))
00603 if ((slash_pos = strchr(slash_pos + 1, '/')))
00604 *slash_pos = '\0';
00605
00606 if (strcasecmp(interface, tmp_interface)) {
00607 ao2_ref(cur, -1);
00608 continue;
00609 }
00610
00611 if (cur->status != status) {
00612 cur->status = status;
00613 if (q->maskmemberstatus) {
00614 ao2_ref(cur, -1);
00615 continue;
00616 }
00617
00618 manager_event(EVENT_FLAG_AGENT, "QueueMemberStatus",
00619 "Queue: %s\r\n"
00620 "Location: %s\r\n"
00621 "MemberName: %s\r\n"
00622 "Membership: %s\r\n"
00623 "Penalty: %d\r\n"
00624 "CallsTaken: %d\r\n"
00625 "LastCall: %d\r\n"
00626 "Status: %d\r\n"
00627 "Paused: %d\r\n",
00628 q->name, cur->interface, cur->membername, cur->dynamic ? "dynamic" : cur->realtime ? "realtime" : "static",
00629 cur->penalty, cur->calls, (int)cur->lastcall, cur->status, cur->paused);
00630 }
00631 ao2_ref(cur, -1);
00632 }
00633 ast_mutex_unlock(&q->lock);
00634 }
00635 AST_LIST_UNLOCK(&queues);
00636
00637 return 0;
00638 }
00639
00640
00641 static void *handle_statechange(struct statechange *sc)
00642 {
00643 struct member_interface *curint;
00644 char *loc;
00645 char *technology;
00646
00647 technology = ast_strdupa(sc->dev);
00648 loc = strchr(technology, '/');
00649 if (loc) {
00650 *loc++ = '\0';
00651 } else {
00652 return NULL;
00653 }
00654
00655 AST_LIST_LOCK(&interfaces);
00656 AST_LIST_TRAVERSE(&interfaces, curint, list) {
00657 char *interface;
00658 char *slash_pos;
00659 interface = ast_strdupa(curint->interface);
00660 if ((slash_pos = strchr(interface, '/')))
00661 if ((slash_pos = strchr(slash_pos + 1, '/')))
00662 *slash_pos = '\0';
00663
00664 if (!strcasecmp(interface, sc->dev))
00665 break;
00666 }
00667 AST_LIST_UNLOCK(&interfaces);
00668
00669 if (!curint) {
00670 if (option_debug > 2)
00671 ast_log(LOG_DEBUG, "Device '%s/%s' changed to state '%d' (%s) but we don't care because they're not a member of any queue.\n", technology, loc, sc->state, devstate2str(sc->state));
00672 return NULL;
00673 }
00674
00675 if (option_debug)
00676 ast_log(LOG_DEBUG, "Device '%s/%s' changed to state '%d' (%s)\n", technology, loc, sc->state, devstate2str(sc->state));
00677
00678 update_status(sc->dev, sc->state);
00679
00680 return NULL;
00681 }
00682
00683
00684
00685
00686 static struct {
00687
00688 unsigned int stop:1;
00689
00690 pthread_t thread;
00691
00692 ast_mutex_t lock;
00693
00694 ast_cond_t cond;
00695
00696 AST_LIST_HEAD_NOLOCK(, statechange) state_change_q;
00697 } device_state = {
00698 .thread = AST_PTHREADT_NULL,
00699 };
00700
00701
00702 static void *device_state_thread(void *data)
00703 {
00704 struct statechange *sc = NULL;
00705
00706 while (!device_state.stop) {
00707 ast_mutex_lock(&device_state.lock);
00708 if (!(sc = AST_LIST_REMOVE_HEAD(&device_state.state_change_q, entry))) {
00709 ast_cond_wait(&device_state.cond, &device_state.lock);
00710 sc = AST_LIST_REMOVE_HEAD(&device_state.state_change_q, entry);
00711 }
00712 ast_mutex_unlock(&device_state.lock);
00713
00714
00715 if (device_state.stop)
00716 break;
00717
00718 if (!sc)
00719 continue;
00720
00721 handle_statechange(sc);
00722
00723 free(sc);
00724 sc = NULL;
00725 }
00726
00727 if (sc)
00728 free(sc);
00729
00730 while ((sc = AST_LIST_REMOVE_HEAD(&device_state.state_change_q, entry)))
00731 free(sc);
00732
00733 return NULL;
00734 }
00735
00736 static int statechange_queue(const char *dev, int state, void *ign)
00737 {
00738 struct statechange *sc;
00739
00740 if (!(sc = ast_calloc(1, sizeof(*sc) + strlen(dev) + 1)))
00741 return 0;
00742
00743 sc->state = state;
00744 strcpy(sc->dev, dev);
00745
00746 ast_mutex_lock(&device_state.lock);
00747 AST_LIST_INSERT_TAIL(&device_state.state_change_q, sc, entry);
00748 ast_cond_signal(&device_state.cond);
00749 ast_mutex_unlock(&device_state.lock);
00750
00751 return 0;
00752 }
00753
00754 static struct member *create_queue_member(const char *interface, const char *membername, int penalty, int paused)
00755 {
00756 struct member *cur;
00757
00758 if ((cur = ao2_alloc(sizeof(*cur), NULL))) {
00759 cur->penalty = penalty;
00760 cur->paused = paused;
00761 ast_copy_string(cur->interface, interface, sizeof(cur->interface));
00762 if (!ast_strlen_zero(membername))
00763 ast_copy_string(cur->membername, membername, sizeof(cur->membername));
00764 else
00765 ast_copy_string(cur->membername, interface, sizeof(cur->membername));
00766 if (!strchr(cur->interface, '/'))
00767 ast_log(LOG_WARNING, "No location at interface '%s'\n", interface);
00768 cur->status = ast_device_state(interface);
00769 }
00770
00771 return cur;
00772 }
00773
00774 static struct call_queue *alloc_queue(const char *queuename)
00775 {
00776 struct call_queue *q;
00777
00778 if ((q = ast_calloc(1, sizeof(*q)))) {
00779 ast_mutex_init(&q->lock);
00780 ast_copy_string(q->name, queuename, sizeof(q->name));
00781 }
00782 return q;
00783 }
00784
00785 static int compress_char(const char c)
00786 {
00787 if (c < 32)
00788 return 0;
00789 else if (c > 96)
00790 return c - 64;
00791 else
00792 return c - 32;
00793 }
00794
00795 static int member_hash_fn(const void *obj, const int flags)
00796 {
00797 const struct member *mem = obj;
00798 const char *chname = strchr(mem->interface, '/');
00799 int ret = 0, i;
00800 if (!chname)
00801 chname = mem->interface;
00802 for (i = 0; i < 5 && chname[i]; i++)
00803 ret += compress_char(chname[i]) << (i * 6);
00804 return ret;
00805 }
00806
00807 static int member_cmp_fn(void *obj1, void *obj2, int flags)
00808 {
00809 struct member *mem1 = obj1, *mem2 = obj2;
00810 return strcmp(mem1->interface, mem2->interface) ? 0 : CMP_MATCH | CMP_STOP;
00811 }
00812
00813 static void init_queue(struct call_queue *q)
00814 {
00815 int i;
00816
00817 q->dead = 0;
00818 q->retry = DEFAULT_RETRY;
00819 q->timeout = -1;
00820 q->maxlen = 0;
00821 q->announcefrequency = 0;
00822 q->announceholdtime = 0;
00823 q->roundingseconds = 0;
00824 q->servicelevel = 0;
00825 q->ringinuse = 1;
00826 q->setinterfacevar = 0;
00827 q->autofill = autofill_default;
00828 q->montype = montype_default;
00829 q->moh[0] = '\0';
00830 q->announce[0] = '\0';
00831 q->context[0] = '\0';
00832 q->monfmt[0] = '\0';
00833 q->periodicannouncefrequency = 0;
00834 q->reportholdtime = 0;
00835 q->monjoin = 0;
00836 q->wrapuptime = 0;
00837 q->joinempty = 0;
00838 q->leavewhenempty = 0;
00839 q->memberdelay = 0;
00840 q->maskmemberstatus = 0;
00841 q->eventwhencalled = 0;
00842 q->weight = 0;
00843 q->timeoutrestart = 0;
00844 if (!q->members)
00845 q->members = ao2_container_alloc(37, member_hash_fn, member_cmp_fn);
00846 q->membercount = 0;
00847 q->found = 1;
00848 ast_copy_string(q->sound_next, "queue-youarenext", sizeof(q->sound_next));
00849 ast_copy_string(q->sound_thereare, "queue-thereare", sizeof(q->sound_thereare));
00850 ast_copy_string(q->sound_calls, "queue-callswaiting", sizeof(q->sound_calls));
00851 ast_copy_string(q->sound_holdtime, "queue-holdtime", sizeof(q->sound_holdtime));
00852 ast_copy_string(q->sound_minutes, "queue-minutes", sizeof(q->sound_minutes));
00853 ast_copy_string(q->sound_seconds, "queue-seconds", sizeof(q->sound_seconds));
00854 ast_copy_string(q->sound_thanks, "queue-thankyou", sizeof(q->sound_thanks));
00855 ast_copy_string(q->sound_lessthan, "queue-less-than", sizeof(q->sound_lessthan));
00856 ast_copy_string(q->sound_reporthold, "queue-reporthold", sizeof(q->sound_reporthold));
00857 ast_copy_string(q->sound_periodicannounce[0], "queue-periodic-announce", sizeof(q->sound_periodicannounce[0]));
00858 for (i = 1; i < MAX_PERIODIC_ANNOUNCEMENTS; i++) {
00859 q->sound_periodicannounce[i][0]='\0';
00860 }
00861 }
00862
00863 static void clear_queue(struct call_queue *q)
00864 {
00865 q->holdtime = 0;
00866 q->callscompleted = 0;
00867 q->callsabandoned = 0;
00868 q->callscompletedinsl = 0;
00869 q->wrapuptime = 0;
00870 }
00871
00872 static int add_to_interfaces(const char *