Add support for using a hint when configuring a state interface using the format...
authorJoshua Colp <jcolp@digium.com>
Tue, 3 Nov 2009 21:16:14 +0000 (21:16 +0000)
committerJoshua Colp <jcolp@digium.com>
Tue, 3 Nov 2009 21:16:14 +0000 (21:16 +0000)
(closes issue #15168)
Reported by: p_lindheimer
Patches:
      queue_extenstate5_1.4.svn.patch uploaded by GameGamer43 (license 894)

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

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

index 80ce8dd..1c8dea3 100644 (file)
@@ -824,20 +824,22 @@ struct queue_ent {
 };
 
 struct member {
-       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 */
-       int dynamic;                        /*!< Are we dynamically added? */
-       int realtime;                       /*!< Is this member realtime? */
-       int status;                         /*!< Status of queue member */
-       int paused;                         /*!< Are we paused (not accepting calls)? */
-       time_t lastcall;                    /*!< When last successful call was hungup */
-       struct call_queue *lastqueue;       /*!< Last queue we received a call */
-       unsigned int dead:1;                /*!< Used to detect members deleted in realtime */
-       unsigned int delme:1;               /*!< Flag to delete entry on reload */
-       char rt_uniqueid[80];               /*!< Unique id of realtime member entry */
+       char interface[80];                  /*!< Technology/Location to dial to reach this member*/
+       char state_exten[AST_MAX_EXTENSION]; /*!< Extension to get state from (if using hint) */
+       char state_context[AST_MAX_CONTEXT]; /*!< Context to use when getting state (if using hint) */
+       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 */
+       int dynamic;                         /*!< Are we dynamically added? */
+       int realtime;                        /*!< Is this member realtime? */
+       int status;                          /*!< Status of queue member */
+       int paused;                          /*!< Are we paused (not accepting calls)? */
+       time_t lastcall;                     /*!< When last successful call was hungup */
+       struct call_queue *lastqueue;        /*!< Last queue we received a call */
+       unsigned int dead:1;                 /*!< Used to detect members deleted in realtime */
+       unsigned int delme:1;                /*!< Flag to delete entry on reload */
+       char rt_uniqueid[80];                /*!< Unique id of realtime member entry */
 };
 
 enum empty_conditions {
@@ -1258,6 +1260,81 @@ static void device_state_cb(const struct ast_event *event, void *unused)
        }
 }
 
+/*! \brief Helper function which converts from extension state to device state values */
+static int extensionstate2devicestate(int state)
+{
+       switch (state) {
+       case AST_EXTENSION_NOT_INUSE:
+               state = AST_DEVICE_NOT_INUSE;
+               break;
+       case AST_EXTENSION_INUSE:
+               state = AST_DEVICE_INUSE;
+               break;
+       case AST_EXTENSION_BUSY:
+               state = AST_DEVICE_BUSY;
+               break;
+       case AST_EXTENSION_RINGING:
+               state = AST_DEVICE_RINGING;
+               break;
+       case AST_EXTENSION_ONHOLD:
+               state = AST_DEVICE_ONHOLD;
+               break;
+       case AST_EXTENSION_UNAVAILABLE:
+               state = AST_DEVICE_UNAVAILABLE;
+               break;
+       case AST_EXTENSION_REMOVED:
+       case AST_EXTENSION_DEACTIVATED:
+       default:
+               state = AST_DEVICE_INVALID;
+               break;
+       }
+
+       return state;
+}
+
+static int extension_state_cb(char *context, char *exten, enum ast_extension_states state, void *data)
+{
+       struct ao2_iterator miter, qiter;
+       struct member *m;
+       struct call_queue *q;
+       int found = 0, device_state = extensionstate2devicestate(state);
+
+       qiter = ao2_iterator_init(queues, 0);
+       while ((q = ao2_iterator_next(&qiter))) {
+               ao2_lock(q);
+
+               miter = ao2_iterator_init(q->members, 0);
+               for (; (m = ao2_iterator_next(&miter)); ao2_ref(m, -1)) {
+                       if (!strcmp(m->state_context, context) && !strcmp(m->state_exten, exten)) {
+                               update_status(q, m, device_state);
+                               ao2_ref(m, -1);
+                               found = 1;
+                               break;
+                       }
+               }
+               ao2_iterator_destroy(&miter);
+
+               ao2_unlock(q);
+               ao2_ref(q, -1);
+       }
+       ao2_iterator_destroy(&qiter);
+
+        if (found) {
+               ast_debug(1, "Extension '%s@%s' changed to state '%d' (%s)\n", exten, context, device_state, ast_devstate2str(device_state));
+       } else {
+               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",
+                         exten, context, device_state, ast_devstate2str(device_state));
+       }
+
+       return 0;
+}
+
+/*! \brief Return the current state of a member */
+static int get_queue_member_status(struct member *cur)
+{
+       return ast_strlen_zero(cur->state_exten) ? ast_device_state(cur->state_interface) : extensionstate2devicestate(ast_extension_state(NULL, cur->state_context, cur->state_exten));
+}
+
 /*! \brief allocate space for new queue member and set fields based on parameters passed */
 static struct member *create_queue_member(const char *interface, const char *membername, int penalty, int paused, const char *state_interface)
 {
@@ -1277,7 +1354,14 @@ static struct member *create_queue_member(const char *interface, const char *mem
                        ast_copy_string(cur->membername, interface, sizeof(cur->membername));
                if (!strchr(cur->interface, '/'))
                        ast_log(LOG_WARNING, "No location at interface '%s'\n", interface);
-               cur->status = ast_device_state(cur->state_interface);
+               if (!strncmp(state_interface, "hint:", 5)) {
+                       char *tmp = ast_strdupa(state_interface), *context = tmp;
+                       char *exten = strsep(&context, "@") + 5;
+
+                       ast_copy_string(cur->state_exten, exten, sizeof(cur->state_exten));
+                       ast_copy_string(cur->state_context, S_OR(context, "default"), sizeof(cur->state_context));
+               }
+               cur->status = get_queue_member_status(cur);
        }
 
        return cur;
@@ -2677,7 +2761,7 @@ static int ring_entry(struct queue_ent *qe, struct callattempt *tmp, int *busies
                tmp->stillgoing = 0;    
 
                ao2_lock(qe->parent);
-               update_status(qe->parent, tmp->member, ast_device_state(tmp->member->state_interface));
+               update_status(qe->parent, tmp->member, get_queue_member_status(tmp->member));
                qe->parent->rrpos++;
                qe->linpos++;
                ao2_unlock(qe->parent);
@@ -2760,7 +2844,7 @@ static int ring_entry(struct queue_ent *qe, struct callattempt *tmp, int *busies
                ast_channel_unlock(qe->chan);
                do_hang(tmp);
                (*busies)++;
-               update_status(qe->parent, tmp->member, ast_device_state(tmp->member->state_interface));
+               update_status(qe->parent, tmp->member, get_queue_member_status(tmp->member));
                return 0;
        } else if (qe->parent->eventwhencalled) {
                char vars[2048];
@@ -2788,7 +2872,7 @@ static int ring_entry(struct queue_ent *qe, struct callattempt *tmp, int *busies
        ast_channel_unlock(tmp->chan);
        ast_channel_unlock(qe->chan);
 
-       update_status(qe->parent, tmp->member, ast_device_state(tmp->member->state_interface));
+       update_status(qe->parent, tmp->member, get_queue_member_status(tmp->member));
        return 1;
 }
 
@@ -6093,7 +6177,7 @@ static int kill_dead_members(void *obj, void *arg, int flags)
                         */
                        q->membercount++;
                }
-               member->status = ast_device_state(member->state_interface);
+               member->status = get_queue_member_status(member);
                return 0;
        } else {
                q->membercount--;
@@ -7480,6 +7564,8 @@ static int unload_module(void)
        if (device_state_sub)
                ast_event_unsubscribe(device_state_sub);
 
+       ast_extension_state_del(0, extension_state_cb);
+
        if ((con = ast_context_find("app_queue_gosub_virtual_context"))) {
                ast_context_remove_extension2(con, "s", 1, NULL, 0);
                ast_context_destroy(con, "app_queue"); /* leave no trace */
@@ -7553,6 +7639,8 @@ static int load_module(void)
                res = -1;
        }
 
+       ast_extension_state_add(NULL, NULL, extension_state_cb, NULL);
+
        ast_realtime_require_field("queue_members", "paused", RQ_INTEGER1, 1, "uniqueid", RQ_UINTEGER2, 5, SENTINEL);
 
        return res ? AST_MODULE_LOAD_DECLINE : 0;
index 244ee08..6752bbf 100644 (file)
@@ -474,6 +474,10 @@ shared_lastcall=no
 ; state notifications, even though the first interface specified is the one
 ; that is actually called.
 ;
+; A hint can also be used in place of the state interface using the format
+; hint:<extension>@<context>. If no context is specified then 'default' will
+; be used.
+;
 ; 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
 ; can be accomplished by explicitly listing them in modules.conf before