Use the same delimited character as the FILTER function in FIELDQTY and CUT.
[asterisk/asterisk.git] / main / devicestate.c
index a1b4c4c..aef21f7 100644 (file)
@@ -25,6 +25,7 @@
  *
  *     \arg \ref AstExtState
  */
+
 /*! \page AstExtState Extension and device states in Asterisk
  *
  *     Asterisk has an internal system that reports states
  *     and reported back.
  *
  *     - Extension states
- *             \arg \ref enum ast_extension_states
+ *             \arg \ref AstENUM ast_extension_states
  *             \arg \ref pbx.c 
  *             \arg \ref pbx.h 
  *     - Structures
- *             - \ref struct ast_state_cb  Callbacks for watchers
+ *             - \ref ast_state_cb struct.  Callbacks for watchers
  *             - Callback ast_state_cb_type
- *             - \ref struct ast_hint
+ *             - \ref ast_hint struct.
  *     - Functions
  *             - ast_extension_state_add()
  *             - ast_extension_state_del()
@@ -166,6 +167,17 @@ static pthread_t change_thread = AST_PTHREADT_NULL;
 /*! \brief Flag for the queue */
 static ast_cond_t change_pending;
 
+/*! \brief Whether or not to cache this device state value */
+enum devstate_cache {
+       /*! Cache this value as it is coming from a device state provider which is
+        *  pushing up state change events to us as they happen */
+       CACHE_ON,
+       /*! Don't cache this result, since it was pulled from the device state provider.
+        *  We only want to cache results from device state providers that are being nice
+        *  and pushing state change events up to us as they happen. */
+       CACHE_OFF,
+};
+
 /* Forward declarations */
 static int getproviderstate(const char *provider, const char *address);
 
@@ -261,18 +273,42 @@ enum ast_device_state ast_parse_device_state(const char *device)
        return res;
 }
 
+static enum ast_device_state devstate_cached(const char *device)
+{
+       enum ast_device_state res = AST_DEVICE_UNKNOWN;
+       struct ast_event *event;
+
+       event = ast_event_get_cached(AST_EVENT_DEVICE_STATE,
+               AST_EVENT_IE_DEVICE, AST_EVENT_IE_PLTYPE_STR, device,
+               AST_EVENT_IE_END);
+
+       if (!event)
+               return res;
+
+       res = ast_event_get_ie_uint(event, AST_EVENT_IE_STATE);
+
+       ast_event_destroy(event);
+
+       return res;
+}
+
 /*! \brief Check device state through channel specific function or generic function */
 enum ast_device_state ast_device_state(const char *device)
 {
        char *buf;
        char *number;
        const struct ast_channel_tech *chan_tech;
-       enum ast_device_state res = AST_DEVICE_UNKNOWN;
+       enum ast_device_state res;
        /*! \brief Channel driver that provides device state */
        char *tech;
        /*! \brief Another provider of device state */
        char *provider = NULL;
-       
+
+       /* If the last known state is cached, just return that */
+       res = devstate_cached(device);
+       if (res != AST_DEVICE_UNKNOWN)
+               return res;
+
        buf = ast_strdupa(device);
        tech = strsep(&buf, "/");
        if (!(number = buf)) {
@@ -284,13 +320,11 @@ enum ast_device_state ast_device_state(const char *device)
        }
 
        if (provider)  {
-               if (option_debug > 2)
-                       ast_log(LOG_DEBUG, "Checking if I can find provider for \"%s\" - number: %s\n", provider, number);
+               ast_debug(3, "Checking if I can find provider for \"%s\" - number: %s\n", provider, number);
                return getproviderstate(provider, number);
        }
 
-       if (option_debug > 3)
-               ast_log(LOG_DEBUG, "No provider found, checking channel drivers for %s - %s\n", tech, number);
+       ast_debug(4, "No provider found, checking channel drivers for %s - %s\n", tech, number);
 
        if (!(chan_tech = ast_get_channel_tech(tech)))
                return AST_DEVICE_INVALID;
@@ -339,7 +373,7 @@ int ast_devstate_prov_del(const char *label)
        AST_RWLIST_TRAVERSE_SAFE_BEGIN(&devstate_provs, devcb, list) {
                if (!strcasecmp(devcb->label, label)) {
                        AST_RWLIST_REMOVE_CURRENT(&devstate_provs, list);
-                       free(devcb);
+                       ast_free(devcb);
                        res = 0;
                        break;
                }
@@ -359,8 +393,7 @@ static int getproviderstate(const char *provider, const char *address)
 
        AST_RWLIST_RDLOCK(&devstate_provs);
        AST_RWLIST_TRAVERSE(&devstate_provs, devprov, list) {
-               if (option_debug > 4)
-                       ast_log(LOG_DEBUG, "Checking provider %s with %s\n", devprov->label, provider);
+               ast_debug(5, "Checking provider %s with %s\n", devprov->label, provider);
 
                if (!strcasecmp(devprov->label, provider)) {
                        res = devprov->callback(address);
@@ -371,18 +404,10 @@ static int getproviderstate(const char *provider, const char *address)
        return res;
 }
 
-/*! \brief Notify callback watchers of change, and notify PBX core for hint updates
-       Normally executed within a separate thread
-*/
-static void do_state_change(const char *device)
+static void devstate_event(const char *device, enum ast_device_state state, enum devstate_cache cache)
 {
-       enum ast_device_state state;
        struct ast_event *event;
 
-       state = ast_device_state(device);
-       if (option_debug > 2)
-               ast_log(LOG_DEBUG, "Changing state for %s - state %d (%s)\n", device, state, devstate2str(state));
-
        if (!(event = ast_event_new(AST_EVENT_DEVICE_STATE,
                        AST_EVENT_IE_DEVICE, AST_EVENT_IE_PLTYPE_STR, device,
                        AST_EVENT_IE_STATE, AST_EVENT_IE_PLTYPE_UINT, state,
@@ -390,28 +415,47 @@ static void do_state_change(const char *device)
                return;
        }
 
-       ast_event_queue_and_cache(event,
-               AST_EVENT_IE_DEVICE, AST_EVENT_IE_PLTYPE_STR,
-               AST_EVENT_IE_END);
+       if (cache == CACHE_ON) {
+               /* Cache this event, replacing an event in the cache with the same
+                * device name if it exists. */
+               ast_event_queue_and_cache(event,
+                       AST_EVENT_IE_DEVICE, AST_EVENT_IE_PLTYPE_STR,
+                       AST_EVENT_IE_END);
+       } else {
+               ast_event_queue(event);
+       }
 }
 
-static int __ast_device_state_changed_literal(char *buf)
+/*! Called by the state change thread to find out what the state is, and then
+ *  to queue up the state change event */
+static void do_state_change(const char *device)
+{
+       enum ast_device_state state;
+
+       state = ast_device_state(device);
+
+       ast_debug(3, "Changing state for %s - state %d (%s)\n", device, state, devstate2str(state));
+
+       devstate_event(device, state, CACHE_OFF);
+}
+
+static int __ast_devstate_changed_literal(enum ast_device_state state, char *buf)
 {
        char *device;
        struct state_change *change;
+       char *tmp = NULL;
 
-       if (option_debug > 2)
-               ast_log(LOG_DEBUG, "Notification of state change to be queued on device/channel %s\n", buf);
+       ast_debug(3, "Notification of state change to be queued on device/channel %s\n", buf);
 
        device = buf;
 
-       if (!strncasecmp(device, "Zap", 3)) {
-               char *tmp = strrchr(device, '-');
-               if (tmp)
-                       *tmp = '\0';
-       }
+       tmp = strrchr(device, '-');
+       if (tmp)
+               *tmp = '\0';
 
-       if (change_thread == AST_PTHREADT_NULL || !(change = ast_calloc(1, sizeof(*change) + strlen(device)))) {
+       if (state != AST_DEVICE_UNKNOWN) {
+               devstate_event(device, state, CACHE_ON);
+       } else if (change_thread == AST_PTHREADT_NULL || !(change = ast_calloc(1, sizeof(*change) + strlen(device)))) {
                /* we could not allocate a change struct, or */
                /* there is no background thread, so process the change now */
                do_state_change(device);
@@ -427,11 +471,34 @@ static int __ast_device_state_changed_literal(char *buf)
        return 1;
 }
 
+int ast_devstate_changed_literal(enum ast_device_state state, const char *dev)
+{
+       char *buf;
+
+       buf = ast_strdupa(dev);
+
+       return __ast_devstate_changed_literal(state, buf);
+}
+
 int ast_device_state_changed_literal(const char *dev)
 {
        char *buf;
+
        buf = ast_strdupa(dev);
-       return __ast_device_state_changed_literal(buf);
+
+       return __ast_devstate_changed_literal(AST_DEVICE_UNKNOWN, buf);
+}
+
+int ast_devstate_changed(enum ast_device_state state, const char *fmt, ...) 
+{
+       char buf[AST_MAX_EXTENSION];
+       va_list ap;
+
+       va_start(ap, fmt);
+       vsnprintf(buf, sizeof(buf), fmt, ap);
+       va_end(ap);
+
+       return __ast_devstate_changed_literal(state, buf);
 }
 
 /*! \brief Accept change notification, add it to change queue */
@@ -443,7 +510,8 @@ int ast_device_state_changed(const char *fmt, ...)
        va_start(ap, fmt);
        vsnprintf(buf, sizeof(buf), fmt, ap);
        va_end(ap);
-       return __ast_device_state_changed_literal(buf);
+
+       return __ast_devstate_changed_literal(AST_DEVICE_UNKNOWN, buf);
 }
 
 /*! \brief Go through the dev state change queue and update changes in the dev state thread */
@@ -464,7 +532,7 @@ static void *do_devstate_changes(void *data)
                while ((current = next)) {
                        next = AST_LIST_NEXT(current, list);
                        do_state_change(current->device);
-                       free(current);
+                       ast_free(current);
                }
        }