Index: apps/app_queue.c =================================================================== --- apps/app_queue.c (revision 84109) +++ apps/app_queue.c (working copy) @@ -258,6 +258,12 @@ /*! \brief queues.conf [general] option */ static int update_cdr = 0; +/*! \brief queues.conf [general] option */ +static int onlyagentperline = 0; + +/*! \brief queues.conf [general] option */ +static int denyparallellogins = 0; + enum queue_result { QUEUE_UNKNOWN = 0, QUEUE_TIMEOUT = 1, @@ -426,6 +432,7 @@ static void update_realtime_members(struct call_queue *q); static int set_member_paused(const char *queuename, const char *interface, const char *reason, int paused); +static void dump_queue_members(struct call_queue* q); static void set_queue_result(struct ast_channel *chan, enum queue_result res) { @@ -915,6 +922,87 @@ return 0; } +static int check_queue_members(const char* interface, const char* membername) +{ + struct call_queue* q; + struct member* mem; + struct ao2_iterator queue_iter; + int ret = 0; + char otherinterface[sizeof(mem->interface)] = ""; + + if (!interface) + return 0; + if (!membername) + return 0; + + queue_iter = ao2_iterator_init(queues, 0); + while ((q = ao2_iterator_next(&queue_iter))) { + struct ao2_iterator member_iter; + ao2_lock(q); + member_iter = ao2_iterator_init(q->members, 0); + while ((mem = ao2_iterator_next(&member_iter))) { + if (onlyagentperline && !strcmp(mem->interface, interface) && strcmp(mem->membername, membername)) { + if ((mem->status != AST_DEVICE_NOT_INUSE) && (mem->status != AST_DEVICE_UNKNOWN)) { + ast_verb(0, "Member %s always logged on line %s with state %s while member %s logging in from the same line. Unable to remove stale member\n", mem->membername, mem->interface, devstate2str(mem->status), membername); + ao2_ref(mem, -1); + continue; + }; + ast_verb(2, "Member %s always logged in on line %s while member %s logging in on the same line, removing stale member\n", mem->membername, mem->interface, membername); + q->membercount--; + manager_event(EVENT_FLAG_AGENT, "QueueMemberRemoved", + "Queue: %s\r\n" + "Location: %s\r\n" + "MemberName: %s\r\n", + q->name, mem->interface, mem->membername); + ao2_unlink(q->members, mem); + ao2_ref(mem, -1); + + if(queue_persistent_members) + dump_queue_members(q); + + ret++; + continue; + }; + + if (denyparallellogins && !strcmp(mem->membername, membername) && strcmp(mem->interface, interface)) { + if ((mem->status != AST_DEVICE_NOT_INUSE) && (mem->status != AST_DEVICE_UNKNOWN)) { + ast_verb(0, "Member %s logging in from %s always logged from %s and %s, unable to remove stale location\n", mem->membername, interface, mem->interface, devstate2str(mem->status)); + ao2_ref(mem, -1); + continue; + }; + ast_verb(2, "Member %s logging in from %s always logged from %s, removing stale location\n", membername, interface, mem->interface); + q->membercount--; + manager_event(EVENT_FLAG_AGENT, "QueueMemberRemoved", + "Queue: %s\r\n" + "Location: %s\r\n" + "MemberName: %s\r\n", + q->name, mem->interface, mem->membername); + ast_copy_string(otherinterface, mem->interface, sizeof(otherinterface)); + ao2_unlink(q->members, mem); + ao2_ref(mem, -1); + + if(queue_persistent_members) + dump_queue_members(q); + + ret++; + continue; + }; + ao2_ref(mem, -1); + } + ao2_unlock(q); + ao2_ref(q, -1); + }; + + if (ret) + remove_from_interfaces(interface); + + if (!ast_strlen_zero(otherinterface)) + remove_from_interfaces(otherinterface); + + return ret; +}; + + static void clear_and_free_interfaces(void) { struct member_interface *curint; @@ -3568,6 +3656,9 @@ } } + if (onlyagentperline || denyparallellogins) + check_queue_members(args.interface, args.membername); + switch (add_to_queue(args.queuename, args.interface, args.membername, penalty, 0, queue_persistent_members)) { case RES_OKAY: ast_queue_log(args.queuename, chan->uniqueid, args.interface, "ADDMEMBER", "%s", ""); @@ -4135,6 +4226,12 @@ update_cdr = 0; if ((general_val = ast_variable_retrieve(cfg, "general", "updatecdr"))) update_cdr = ast_true(general_val); + onlyagentperline = 0; + if ((general_val = ast_variable_retrieve(cfg, "general", "onlyagentperline"))) + onlyagentperline = ast_true(general_val); + denyparallellogins = 0; + if ((general_val = ast_variable_retrieve(cfg, "general", "denyparallellogins"))) + denyparallellogins = ast_true(general_val); } else { /* Define queue */ /* Look for an existing one */ ast_copy_string(tmpq.name, cat, sizeof(tmpq.name)); @@ -4604,6 +4701,9 @@ else paused = abs(ast_true(paused_s)); + if (onlyagentperline || denyparallellogins) + check_queue_members(interface, membername); + switch (add_to_queue(queuename, interface, membername, penalty, paused, queue_persistent_members)) { case RES_OKAY: ast_queue_log(queuename, "MANAGER", interface, "ADDMEMBER", "%s", ""); @@ -4775,6 +4875,9 @@ membername = a->argv[9]; } + if (onlyagentperline || denyparallellogins) + check_queue_members(interface, membername); + switch (add_to_queue(queuename, interface, membername, penalty, 0, queue_persistent_members)) { case RES_OKAY: ast_queue_log(queuename, "CLI", interface, "ADDMEMBER", "%s", ""); Index: configs/queues.conf.sample =================================================================== --- configs/queues.conf.sample (revision 84109) +++ configs/queues.conf.sample (working copy) @@ -54,7 +54,31 @@ ; ; updatecdr = no +; Parallel logins options ; +; Note: next options works only when agent logged onto queue +; with his membername set to something distinctive. +; For example, login procedure should contain not just +; AddQueueMember(queue) +; but +; AddQueueMember(queue,,,,Agent/${AGENTID}) +; where AGENTID is his unique ID. +; +; Well, some call agents forgetting to log out of queues after his +; shift ends. 'onlyagentperline' option helps in that situation - +; when agent logs in on some line, any other agents logged +; from this line is automatically 'logged off' (removed from all +; queues). +; +; onlyagentperline = no +; +; Next option addresses situations when agent, always logged from +; some interface, logs in from another one. To logoff such agents +; from 'stale' lines automatically - set denyparallellogins to yes. +; +; denyparallellogins = no + +; ; Note that a timeout to fail out of a queue may be passed as part of ; an application call from extensions.conf: ; Queue(queuename|[options]|[optionalurl]|[announceoverride]|[timeout])