Adding the option of specifying a second interface in a member definition for a queue...
authorMark Michelson <mmichelson@digium.com>
Tue, 8 Jan 2008 21:18:32 +0000 (21:18 +0000)
committerMark Michelson <mmichelson@digium.com>
Tue, 8 Jan 2008 21:18:32 +0000 (21:18 +0000)
will monitor this second device's state for the member, even though it actually calls the first
interface. This ability has been added for statically defined queue members, realtime queue members,
and dynamic queue members added through the CLI, dialplan, or manager.

(closes issue #11603, reported by acidv)

git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@97203 65c4cc65-6c06-0410-ace0-fbb531ad65f3

CHANGES
apps/app_queue.c
configs/queues.conf.sample

diff --git a/CHANGES b/CHANGES
index 95ddd57..ad60f60 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -265,6 +265,9 @@ Queue changes
   * Added new channel variable QUEUE_MIN_PENALTY
   * QUEUE_MAX_PENALTY and QUEUE_MIN_PENALTY may be adjusted in mid-call by defining
      rules in queuerules.conf. See configs/queuerules.conf.sample for details
+  * Added a new parameter for member definition, called state_interface. This may be
+    used so that a member may be called via one interface but have a different interface's
+       device state reported.
 
 MeetMe Changes
 --------------
index 1185b36..8e35d7d 100644 (file)
@@ -352,7 +352,8 @@ struct queue_ent {
 };
 
 struct member {
-       char interface[80];                 /*!< Technology/Location */
+       char interface[80];                 /*!< Technology/Location to dial to reach this member*/
+       char state_interface[80];           /*!< Technology/Location from which to read devicestate changes */
        char membername[80];                /*!< Member name to use in queue logs */
        int penalty;                        /*!< Are we a last resort? */
        int calls;                          /*!< Number of calls serviced by this member */
@@ -690,7 +691,7 @@ static void *handle_statechange(struct statechange *sc)
                while ((cur = ao2_iterator_next(&mem_iter))) {
                        char *interface;
                        char *slash_pos;
-                       interface = ast_strdupa(cur->interface);
+                       interface = ast_strdupa(cur->state_interface);
                        if ((slash_pos = strchr(interface, '/')))
                                if ((slash_pos = strchr(slash_pos + 1, '/')))
                                        *slash_pos = '\0';
@@ -815,7 +816,7 @@ static void device_state_cb(const struct ast_event *event, void *unused)
        statechange_queue(device, state);
 }
 
-static struct member *create_queue_member(const char *interface, const char *membername, int penalty, int paused)
+static struct member *create_queue_member(const char *interface, const char *membername, int penalty, int paused, const char *state_interface)
 {
        struct member *cur;
        
@@ -823,6 +824,10 @@ static struct member *create_queue_member(const char *interface, const char *mem
                cur->penalty = penalty;
                cur->paused = paused;
                ast_copy_string(cur->interface, interface, sizeof(cur->interface));
+               if (!ast_strlen_zero(state_interface))
+                       ast_copy_string(cur->state_interface, state_interface, sizeof(cur->state_interface));
+               else
+                       ast_copy_string(cur->state_interface, interface, sizeof(cur->state_interface));
                if (!ast_strlen_zero(membername))
                        ast_copy_string(cur->membername, membername, sizeof(cur->membername));
                else
@@ -958,20 +963,20 @@ static int interface_exists_global(const char *interface)
 {
        struct call_queue *q;
        struct member *mem, tmpmem;
-       struct ao2_iterator queue_iter;
+       struct ao2_iterator queue_iter, mem_iter;
        int ret = 0;
 
        ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface));
        queue_iter = ao2_iterator_init(queues, 0);
        while ((q = ao2_iterator_next(&queue_iter))) {
-
                ao2_lock(q);
-               if ((mem = ao2_find(q->members, &tmpmem, OBJ_POINTER))) {
-                       ao2_ref(mem, -1);
-                       ao2_unlock(q);
-                       queue_unref(q);
-                       ret = 1;
-                       break;
+               mem_iter = ao2_iterator_init(q->members, 0);
+               while ((mem = ao2_iterator_next(&mem_iter))) { 
+                       if (!strcasecmp(mem->state_interface, interface)) {
+                               ao2_ref(mem, -1);
+                               ret = 1;
+                               break;
+                       }
                }
                ao2_unlock(q);
                queue_unref(q);
@@ -991,7 +996,7 @@ static int remove_from_interfaces(const char *interface)
                                ast_debug(1, "Removing %s from the list of interfaces that make up all of our queue members.\n", interface);
                                AST_LIST_REMOVE_CURRENT(list);
                                ast_free(curint);
-                       }
+                       } 
                        break;
                }
        }
@@ -1256,7 +1261,7 @@ static void queue_set_param(struct call_queue *q, const char *param, const char
        }
 }
 
-static void rt_handle_member_record(struct call_queue *q, char *interface, const char *membername, const char *penalty_str, const char *paused_str)
+static void rt_handle_member_record(struct call_queue *q, char *interface, const char *membername, const char *penalty_str, const char *paused_str, const char* state_interface)
 {
        struct member *m, tmpmem;
        int penalty = 0;
@@ -1280,10 +1285,10 @@ static void rt_handle_member_record(struct call_queue *q, char *interface, const
 
        /* Create a new one if not found, else update penalty */
        if (!m) {
-               if ((m = create_queue_member(interface, membername, penalty, paused))) {
+               if ((m = create_queue_member(interface, membername, penalty, paused, state_interface))) {
                        m->dead = 0;
                        m->realtime = 1;
-                       add_to_interfaces(interface);
+                       add_to_interfaces(state_interface);
                        ao2_link(q->members, m);
                        ao2_ref(m, -1);
                        m = NULL;
@@ -1293,6 +1298,11 @@ static void rt_handle_member_record(struct call_queue *q, char *interface, const
                m->dead = 0;    /* Do not delete this one. */
                if (paused_str)
                        m->paused = paused;
+               if (strcasecmp(state_interface, m->state_interface)) {
+                       remove_from_interfaces(m->state_interface);
+                       ast_copy_string(m->state_interface, state_interface, sizeof(m->state_interface));
+                       add_to_interfaces(m->state_interface);
+               }
                m->penalty = penalty;
                ao2_ref(m, -1);
        }
@@ -1307,7 +1317,7 @@ static void free_members(struct call_queue *q, int all)
        while ((cur = ao2_iterator_next(&mem_iter))) {
                if (all || !cur->dynamic) {
                        ao2_unlink(q->members, cur);
-                       remove_from_interfaces(cur->interface);
+                       remove_from_interfaces(cur->state_interface);
                        q->membercount--;
                }
                ao2_ref(cur, -1);
@@ -1436,7 +1446,8 @@ static struct call_queue *find_queue_by_name_rt(const char *queuename, struct as
                rt_handle_member_record(q, interface,
                        ast_variable_retrieve(member_config, interface, "membername"),
                        ast_variable_retrieve(member_config, interface, "penalty"),
-                       ast_variable_retrieve(member_config, interface, "paused"));
+                       ast_variable_retrieve(member_config, interface, "paused"),
+                       ast_variable_retrieve(member_config, interface, "state_interface"));
        }
 
        /* Delete all realtime members that have been deleted in DB. */
@@ -1444,9 +1455,7 @@ static struct call_queue *find_queue_by_name_rt(const char *queuename, struct as
        while ((m = ao2_iterator_next(&mem_iter))) {
                if (m->dead) {
                        ao2_unlink(q->members, m);
-                       ao2_unlock(q);
-                       remove_from_interfaces(m->interface);
-                       ao2_lock(q);
+                       remove_from_interfaces(m->state_interface);
                        q->membercount--;
                }
                ao2_ref(m, -1);
@@ -1549,7 +1558,8 @@ static void update_realtime_members(struct call_queue *q)
                rt_handle_member_record(q, interface,
                        S_OR(ast_variable_retrieve(member_config, interface, "membername"), interface),
                        ast_variable_retrieve(member_config, interface, "penalty"),
-                       ast_variable_retrieve(member_config, interface, "paused"));
+                       ast_variable_retrieve(member_config, interface, "paused"),
+                       ast_variable_retrieve(member_config, interface, "state_interface"));
        }
 
        /* Delete all realtime members that have been deleted in DB. */
@@ -1557,9 +1567,7 @@ static void update_realtime_members(struct call_queue *q)
        while ((m = ao2_iterator_next(&mem_iter))) {
                if (m->dead) {
                        ao2_unlink(q->members, m);
-                       ao2_unlock(q);
-                       remove_from_interfaces(m->interface);
-                       ao2_lock(q);
+                       remove_from_interfaces(m->state_interface);
                        q->membercount--;
                }
                ao2_ref(m, -1);
@@ -3578,6 +3586,7 @@ static int remove_from_queue(const char *queuename, const char *interface)
                                "MemberName: %s\r\n",
                                q->name, mem->interface, mem->membername);
                        ao2_unlink(q->members, mem);
+                       remove_from_interfaces(mem->state_interface);
                        ao2_ref(mem, -1);
 
                        if (queue_persistent_members)
@@ -3591,14 +3600,11 @@ static int remove_from_queue(const char *queuename, const char *interface)
                queue_unref(q);
        }
 
-       if (res == RES_OKAY)
-               remove_from_interfaces(interface);
-
        return res;
 }
 
 
-static int add_to_queue(const char *queuename, const char *interface, const char *membername, int penalty, int paused, int dump)
+static int add_to_queue(const char *queuename, const char *interface, const char *membername, int penalty, int paused, int dump, const char *state_interface)
 {
        struct call_queue *q;
        struct member *new_member, *old_member;
@@ -3613,8 +3619,8 @@ static int add_to_queue(const char *queuename, const char *interface, const char
 
        ao2_lock(q);
        if ((old_member = interface_exists(q, interface)) == NULL) {
-               add_to_interfaces(interface);
-               if ((new_member = create_queue_member(interface, membername, penalty, paused))) {
+               if ((new_member = create_queue_member(interface, membername, penalty, paused, state_interface))) {
+                       add_to_interfaces(state_interface);
                        new_member->dynamic = 1;
                        ao2_link(q->members, new_member);
                        q->membercount++;
@@ -3797,6 +3803,7 @@ static void reload_queue_members(void)
        char *member;
        char *interface;
        char *membername = NULL;
+       char *state_interface;
        char *penalty_tok;
        int penalty = 0;
        char *paused_tok;
@@ -3846,6 +3853,7 @@ static void reload_queue_members(void)
                        penalty_tok = strsep(&member, ";");
                        paused_tok = strsep(&member, ";");
                        membername = strsep(&member, ";");
+                       state_interface = strsep(&member, ";");
 
                        if (!penalty_tok) {
                                ast_log(LOG_WARNING, "Error parsing persistent member string for '%s' (penalty)\n", queue_name);
@@ -3866,12 +3874,10 @@ static void reload_queue_members(void)
                                ast_log(LOG_WARNING, "Error converting paused: %s: Expected 0 or 1.\n", paused_tok);
                                break;
                        }
-                       if (ast_strlen_zero(membername))
-                               membername = interface;
 
                        ast_debug(1, "Reload Members: Queue: %s  Member: %s  Name: %s  Penalty: %d  Paused: %d\n", queue_name, interface, membername, penalty, paused);
                        
-                       if (add_to_queue(queue_name, interface, membername, penalty, paused, 0) == RES_OUTOFMEMORY) {
+                       if (add_to_queue(queue_name, interface, membername, penalty, paused, 0, state_interface) == RES_OUTOFMEMORY) {
                                ast_log(LOG_ERROR, "Out of Memory when reloading persistent queue member\n");
                                break;
                        }
@@ -4020,6 +4026,7 @@ static int aqm_exec(struct ast_channel *chan, void *data)
                AST_APP_ARG(penalty);
                AST_APP_ARG(options);
                AST_APP_ARG(membername);
+               AST_APP_ARG(state_interface);
        );
        int penalty = 0;
 
@@ -4046,7 +4053,7 @@ static int aqm_exec(struct ast_channel *chan, void *data)
                }
        }
 
-       switch (add_to_queue(args.queuename, args.interface, args.membername, penalty, 0, queue_persistent_members)) {
+       switch (add_to_queue(args.queuename, args.interface, args.membername, penalty, 0, queue_persistent_members, args.state_interface)) {
        case RES_OKAY:
                ast_queue_log(args.queuename, chan->uniqueid, args.interface, "ADDMEMBER", "%s", "");
                ast_log(LOG_NOTICE, "Added interface '%s' to queue '%s'\n", args.interface, args.queuename);
@@ -4803,7 +4810,7 @@ static int reload_queues(int reload)
        int new;
        const char *general_val = NULL;
        char parse[80];
-       char *interface;
+       char *interface, *state_interface;
        char *membername = NULL;
        int penalty;
        struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
@@ -4812,6 +4819,7 @@ static int reload_queues(int reload)
                AST_APP_ARG(interface);
                AST_APP_ARG(penalty);
                AST_APP_ARG(membername);
+               AST_APP_ARG(state_interface);
        );
 
        /*First things first. Let's load queuerules.conf*/
@@ -4880,8 +4888,10 @@ static int reload_queues(int reload)
                                /* Check if a queue with this name already exists */
                                if (q->found) {
                                        ast_log(LOG_WARNING, "Queue '%s' already defined! Skipping!\n", cat);
-                                       if (!new)
+                                       if (!new) {
                                                ao2_unlock(q);
+                                               queue_unref(q);
+                                       }
                                        continue;
                                }
                                /* Due to the fact that the "linear" strategy will have a different allocation
@@ -4933,11 +4943,22 @@ static int reload_queues(int reload)
                                                        while (*membername && *membername < 33) membername++;
                                                }
 
+                                               if (!ast_strlen_zero(args.state_interface)) {
+                                                       state_interface = args.state_interface;
+                                                       while (*state_interface && *state_interface < 33) state_interface++;
+                                               } else
+                                                       state_interface = interface;
+
                                                /* Find the old position in the list */
                                                ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface));
                                                cur = ao2_find(q->members, &tmpmem, OBJ_POINTER | OBJ_UNLINK);
-
-                                               newm = create_queue_member(interface, membername, penalty, cur ? cur->paused : 0);
+                                               /* Only attempt removing from interfaces list if the new state_interface is different than the old one */
+                                               if (cur && strcasecmp(cur->state_interface, state_interface)) {
+                                                       remove_from_interfaces(cur->state_interface);
+                                               }
+                                               newm = create_queue_member(interface, membername, penalty, cur ? cur->paused : 0, state_interface);
+                                               if (!cur || (cur && strcasecmp(cur->state_interface, state_interface)))
+                                                       add_to_interfaces(state_interface);
                                                ao2_link(q->members, newm);
                                                ao2_ref(newm, -1);
                                                newm = NULL;
@@ -4945,8 +4966,6 @@ static int reload_queues(int reload)
                                                if (cur)
                                                        ao2_ref(cur, -1);
                                                else {
-                                                       /* Add them to the master int list if necessary */
-                                                       add_to_interfaces(interface);
                                                        q->membercount++;
                                                }
                                        } else {
@@ -4961,16 +4980,16 @@ static int reload_queues(int reload)
                                                ao2_ref(cur, -1);
                                                continue;
                                        }
-
-                                       remove_from_interfaces(cur->interface);
+                                       ast_log(LOG_DEBUG, "%s in queue marked as delme, we should be deleting...\n", cur->interface);
                                        q->membercount--;
                                        ao2_unlink(q->members, cur);
+                                       remove_from_interfaces(cur->interface);
                                        ao2_ref(cur, -1);
                                }
 
                                if (new) {
                                        ao2_link(queues, q);
-                               } else
+                               } else 
                                        ao2_unlock(q);
                                queue_unref(q);
                        }
@@ -5352,7 +5371,7 @@ static int manager_queues_status(struct mansession *s, const struct message *m)
 
 static int manager_add_queue_member(struct mansession *s, const struct message *m)
 {
-       const char *queuename, *interface, *penalty_s, *paused_s, *membername;
+       const char *queuename, *interface, *penalty_s, *paused_s, *membername, *state_interface;
        int paused, penalty = 0;
 
        queuename = astman_get_header(m, "Queue");
@@ -5360,6 +5379,7 @@ static int manager_add_queue_member(struct mansession *s, const struct message *
        penalty_s = astman_get_header(m, "Penalty");
        paused_s = astman_get_header(m, "Paused");
        membername = astman_get_header(m, "MemberName");
+       state_interface = astman_get_header(m, "StateInterface");
 
        if (ast_strlen_zero(queuename)) {
                astman_send_error(s, m, "'Queue' not specified.");
@@ -5381,7 +5401,7 @@ static int manager_add_queue_member(struct mansession *s, const struct message *
        else
                paused = abs(ast_true(paused_s));
 
-       switch (add_to_queue(queuename, interface, membername, penalty, paused, queue_persistent_members)) {
+       switch (add_to_queue(queuename, interface, membername, penalty, paused, queue_persistent_members, state_interface)) {
        case RES_OKAY:
                ast_queue_log(queuename, "MANAGER", interface, "ADDMEMBER", "%s", "");
                astman_send_ack(s, m, "Added interface to queue");
@@ -5537,26 +5557,28 @@ static int manager_queue_member_penalty(struct mansession *s, const struct messa
 
 static char *handle_queue_add_member(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 {
-       char *queuename, *interface, *membername = NULL;
+       char *queuename, *interface, *membername = NULL, *state_interface;
        int penalty;
 
        switch ( cmd ) {
        case CLI_INIT:
                e->command = "queue add member";
                e->usage =
-                       "Usage: queue add member <channel> to <queue> [penalty <penalty>]\n"; 
+                       "Usage: queue add member <channel> to <queue> [[[penalty <penalty>] as <membername>] state_interface <interface>]\n"; 
                return NULL;
        case CLI_GENERATE:
                return complete_queue_add_member(a->line, a->word, a->pos, a->n);
        }
 
-       if ((a->argc != 6) && (a->argc != 8) && (a->argc != 10)) {
+       if ((a->argc != 6) && (a->argc != 8) && (a->argc != 10) && (a->argc != 12)) {
                return CLI_SHOWUSAGE;
        } else if (strcmp(a->argv[4], "to")) {
                return CLI_SHOWUSAGE;
-       } else if ((a->argc == 8) && strcmp(a->argv[6], "penalty")) {
+       } else if ((a->argc >= 8) && strcmp(a->argv[6], "penalty")) {
+               return CLI_SHOWUSAGE;
+       } else if ((a->argc >= 10) && strcmp(a->argv[8], "as")) {
                return CLI_SHOWUSAGE;
-       } else if ((a->argc == 10) && strcmp(a->argv[8], "as")) {
+       } else if ((a->argc == 12) && strcmp(a->argv[10], "state_interface")) {
                return CLI_SHOWUSAGE;
        }
 
@@ -5580,7 +5602,11 @@ static char *handle_queue_add_member(struct ast_cli_entry *e, int cmd, struct as
                membername = a->argv[9];
        }
 
-       switch (add_to_queue(queuename, interface, membername, penalty, 0, queue_persistent_members)) {
+       if (a->argc >= 12) {
+               state_interface = a->argv[11];
+       }
+
+       switch (add_to_queue(queuename, interface, membername, penalty, 0, queue_persistent_members, state_interface)) {
        case RES_OKAY:
                ast_queue_log(queuename, "CLI", interface, "ADDMEMBER", "%s", "");
                ast_cli(a->fd, "Added interface '%s' to queue '%s'\n", interface, queuename);
index 7fe1486..92c3237 100644 (file)
@@ -375,7 +375,10 @@ shared_lastcall=no
 ; entries with higher penalties are considered last.  An optional member
 ; name may also be specified after a second comma, which is used in log
 ; messages as a "friendly name".  Multiple interfaces may share a single
-; member name.
+; member name. An optional state interface may be specified after a third
+; comma. This interface will be the one for which app_queue receives device
+; state notifications, even though the first interface specified is the one
+; that is actually called.
 ;
 ; It is important to ensure that channel drivers used for members are loaded
 ; before app_queue.so itself or they may be marked invalid until reload. This
@@ -386,6 +389,7 @@ shared_lastcall=no
 ;member => Zap/3,10,Bob Johnson
 ;member => Agent/1001
 ;member => Agent/1002
+;member => Local/1000@default,0,John Smith,SIP/1000
 
 ;
 ; Note that using agent groups is probably not what you want.  Strategies do