changeset 243:1e98f511dc00

Fix the command pipe (need review on the testing) and wip on the metadata stuff.
author Louis Opter <kalessin@kalessin.fr>
date Fri, 14 Aug 2015 02:31:38 -0700
parents d23d6fbb50df
children 4318373d925c
files fix_command_pipe_read_loop.patch implement_some_metadata_packet_types.patch series
diffstat 3 files changed, 466 insertions(+), 19 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/fix_command_pipe_read_loop.patch	Fri Aug 14 02:31:38 2015 -0700
@@ -0,0 +1,152 @@
+# HG changeset patch
+# Parent  61d444c8e81f6c00f8347fd2d3fa9cbc9f7faef4
+
+diff --git a/core/pipe.c b/core/pipe.c
+--- a/core/pipe.c
++++ b/core/pipe.c
+@@ -41,7 +41,7 @@
+     SLIST_HEAD_INITIALIZER(&lgtd_command_pipes);
+ 
+ static void
+-lgtd_command_pipe_close(struct lgtd_command_pipe *pipe)
++_lgtd_command_pipe_close(struct lgtd_command_pipe *pipe)
+ {
+     assert(pipe);
+ 
+@@ -49,16 +49,24 @@
+     if (pipe->fd != -1) {
+         close(pipe->fd);
+     }
+-    unlink(pipe->path);
+     SLIST_REMOVE(&lgtd_command_pipes, pipe, lgtd_command_pipe, link);
+     evbuffer_free(pipe->read_buf);
+     event_free(pipe->read_ev);
+-
+-    lgtd_info("closed command pipe %s", pipe->path);
+     free(pipe);
+ }
+ 
+ static void
++lgtd_command_pipe_close(struct lgtd_command_pipe *pipe)
++{
++    const char *path = pipe->path;
++    _lgtd_command_pipe_close(pipe);
++    unlink(path);
++    lgtd_info("closed command pipe %s", path);
++}
++
++static void lgtd_command_pipe_reset(struct lgtd_command_pipe *);
++
++static void
+ lgtd_command_pipe_read_callback(evutil_socket_t socket, short events, void *ctx)
+ {
+     assert(ctx);
+@@ -70,7 +78,6 @@
+     struct lgtd_command_pipe *pipe = ctx;
+ 
+     bool drain = false;
+-    int read = 0;
+     for (int nbytes = evbuffer_read(pipe->read_buf, pipe->fd, -1);
+          nbytes;
+          nbytes = evbuffer_read(pipe->read_buf, pipe->fd, -1)) {
+@@ -80,16 +87,12 @@
+             }
+             if (errno != EAGAIN) {
+                 lgtd_warn("read error on command pipe %s", pipe->path);
+-                const char *path = pipe->path;
+-                lgtd_command_pipe_close(pipe);
+-                lgtd_command_pipe_open(path);
+-                return;
++                break;  // go back to the event loop
+             }
+-            continue;
++            return;
+         }
+ 
+         if (!drain) {
+-            read += nbytes;
+         next_request:
+             (void)0;
+             const char *buf = (char *)evbuffer_pullup(pipe->read_buf, -1);
+@@ -127,25 +130,22 @@
+                 jsmn_init(&pipe->client.jsmn_ctx);
+                 int request_size = pipe->client.jsmn_tokens[0].end;
+                 evbuffer_drain(pipe->read_buf, request_size);
+-                read -= request_size;
+-                if (read) {
++                if (request_size < bufsz) {
+                     goto next_request;
+                 }
+                 break;
+             }
+-        } else {
+-            evbuffer_drain(pipe->read_buf, read + nbytes);
+-            read = 0;
++        }
++
++        if (drain) {
++            ssize_t bufsz = evbuffer_get_length(pipe->read_buf);
++            evbuffer_drain(pipe->read_buf, bufsz);
++            drain = false;
++            jsmn_init(&pipe->client.jsmn_ctx);
+         }
+     }
+ 
+-    if (read) {
+-        lgtd_debug(
+-            "pipe %s: discarding %d bytes of unusable data", pipe->path, read
+-        );
+-        evbuffer_drain(pipe->read_buf, read);
+-    }
+-    jsmn_init(&pipe->client.jsmn_ctx);
++    lgtd_command_pipe_reset(pipe);
+ }
+ 
+ static mode_t
+@@ -156,8 +156,8 @@
+     return mask;
+ }
+ 
+-bool
+-lgtd_command_pipe_open(const char *path)
++static bool
++_lgtd_command_pipe_open(const char *path)
+ {
+     assert(path);
+ 
+@@ -218,8 +218,6 @@
+         goto error;
+     }
+ 
+-    lgtd_info("command pipe ready at %s", pipe->path);
+-
+     SLIST_INSERT_HEAD(&lgtd_command_pipes, pipe, link);
+ 
+     return true;
+@@ -239,6 +237,26 @@
+     return false;
+ }
+ 
++static void
++lgtd_command_pipe_reset(struct lgtd_command_pipe *pipe)
++{
++    const char *path = pipe->path;
++    _lgtd_command_pipe_close(pipe);
++    if (!_lgtd_command_pipe_open(path)) {
++        lgtd_warn("can't re-open pipe %s", path);
++    }
++}
++
++bool
++lgtd_command_pipe_open(const char *path)
++{
++    if (_lgtd_command_pipe_open(path)) {
++        lgtd_info("command pipe ready at %s", path);
++        return true;
++    }
++    return false;
++}
++
+ void
+ lgtd_command_pipe_close_all(void)
+ {
--- a/implement_some_metadata_packet_types.patch	Thu Aug 13 02:29:45 2015 -0700
+++ b/implement_some_metadata_packet_types.patch	Fri Aug 14 02:31:38 2015 -0700
@@ -1,5 +1,5 @@
 # HG changeset patch
-# Parent  42808e1e42e717f87163cdaf63d9c72303da2d9f
+# Parent  c861815ea8cd4d7a6feba313ce8edab140f769f6
 Handle various informational packet types
 
 - MCU state & firmware info;
@@ -80,7 +80,7 @@
 diff --git a/core/log.c b/core/log.c
 --- a/core/log.c
 +++ b/core/log.c
-@@ -52,14 +52,7 @@
+@@ -53,14 +53,7 @@
      if (!localtime_r(&now.tv_sec, &tm_now)) {
          goto error;
      }
@@ -96,7 +96,7 @@
      return;
  error:
      strbuf[0] = '\0';
-@@ -110,6 +103,26 @@
+@@ -121,6 +114,26 @@
  }
  
  void
@@ -149,10 +149,10 @@
 +            "{"
 +                "\"_lifx\":{"
 +                    "\"gateway\":{"
-+                        "\"url\":\"tcp://[%s]:%hu\","
++                        "\"addr\":\"%s\","
 +                        "\"latency\":%ju"
 +                    "},",
-+            bulb->gw->ip_addr, bulb->gw->port,
++            lgtd_addrtoa(bulb->gw->addr),
 +            (uintmax_t)LGTD_LIFX_GATEWAY_LATENCY(bulb->gw)
 +        );
 +
@@ -191,8 +191,8 @@
 +        LGTD_SNPRINTF_APPEND(
 +            buf, i, (int)sizeof(buf),
 +                "\"product_info\":{"
-+                    "\"vendor_id\":%#x,"
-+                    "\"product_id\":%#x,"
++                    "\"vendor_id\":%x,"
++                    "\"product_id\":%x,"
 +                    "\"version\":%u"
 +                "},",
 +            bulb->product_info.vendor_id,
@@ -265,7 +265,14 @@
 diff --git a/lifx/bulb.c b/lifx/bulb.c
 --- a/lifx/bulb.c
 +++ b/lifx/bulb.c
-@@ -39,6 +39,8 @@
+@@ -34,11 +34,15 @@
+ #include "gateway.h"
+ #include "core/daemon.h"
+ #include "core/stats.h"
++#include "core/proto.h"
++#include "core/router.h"
+ #include "core/lightsd.h"
+ 
  struct lgtd_lifx_bulb_map lgtd_lifx_bulbs_table =
      RB_INITIALIZER(&lgtd_lifx_bulbs_table);
  
@@ -274,7 +281,34 @@
  struct lgtd_lifx_bulb *
  lgtd_lifx_bulb_get(const uint8_t *addr)
  {
-@@ -154,3 +156,53 @@
+@@ -68,6 +72,10 @@
+ 
+     bulb->last_light_state_at = lgtd_time_monotonic_msecs();
+ 
++    lgtd_router_send_to_device(bulb, LGTD_LIFX_GET_VERSION, NULL);
++    lgtd_router_send_to_device(bulb, LGTD_LIFX_GET_MESH_FIRMWARE, NULL);
++    lgtd_router_send_to_device(bulb, LGTD_LIFX_GET_WIFI_FIRMWARE_STATE, NULL);
++
+     return bulb;
+ }
+ 
+@@ -123,6 +131,7 @@
+         LGTD_STATS_ADD_AND_UPDATE_PROCTITLE(
+             bulbs_powered_on, state->power == LGTD_LIFX_POWER_ON ? 1 : -1
+         );
++        lgtd_router_send_to_device(bulb, LGTD_LIFX_GET_INFO, NULL);
+     }
+ 
+     lgtd_lifx_gateway_update_tag_refcounts(bulb->gw, bulb->state.tags, state->tags);
+@@ -140,6 +149,7 @@
+         LGTD_STATS_ADD_AND_UPDATE_PROCTITLE(
+             bulbs_powered_on, power == LGTD_LIFX_POWER_ON ? 1 : -1
+         );
++        lgtd_router_send_to_device(bulb, LGTD_LIFX_GET_INFO, NULL);
+     }
+ 
+     bulb->state.power = power;
+@@ -154,3 +164,53 @@
  
      bulb->state.tags = tags;
  }
@@ -417,7 +451,81 @@
 diff --git a/lifx/gateway.c b/lifx/gateway.c
 --- a/lifx/gateway.c
 +++ b/lifx/gateway.c
-@@ -489,12 +489,8 @@
+@@ -15,12 +15,15 @@
+ // You should have received a copy of the GNU General Public License
+ // along with lighstd.  If not, see <http://www.gnu.org/licenses/>.
+ 
++#include <sys/ioctl.h>
+ #include <sys/queue.h>
++#include <sys/socket.h>
+ #include <sys/tree.h>
+ #include <assert.h>
+ #include <endian.h>
+ #include <err.h>
+ #include <errno.h>
++#include <net/if_arp.h>
+ #include <stdarg.h>
+ #include <stdbool.h>
+ #include <stdint.h>
+@@ -275,6 +278,27 @@
+     return bulb;
+ }
+ 
++// TODO: make a separate shared library to resolve ip addresses to mac
++// addresses:
++static bool
++lgtd_lifx_gateway_resolve_ipv4_addr(int socket,
++                                    const struct sockaddr_in *sin,
++                                    uint8_t *hw_addr)
++{
++    assert(sin);
++    assert(hw_addr);
++    assert(socket >= 0);
++
++    struct arpreq req;
++    memset(&req, 0, sizeof(req));
++    memcpy(&req.arp_pa, sin, sizeof(*sin));
++    if (!ioctl(socket, SIOCGARP, &req)) {
++        memcpy(hw_addr, req.arp_pa.sa_data, LGTD_LIFX_ADDR_LENGTH);
++        return true;
++    }
++    return false;
++}
++
+ struct lgtd_lifx_gateway *
+ lgtd_lifx_gateway_open(const struct sockaddr_storage *peer,
+                        ev_socklen_t addrlen,
+@@ -310,10 +334,20 @@
+     gw->refresh_ev = evtimer_new(
+         lgtd_ev_base, lgtd_lifx_gateway_refresh_callback, gw
+     );
++
+     memcpy(&gw->peer, peer, sizeof(gw->peer));
+     lgtd_sockaddrtoa(peer, gw->ip_addr, sizeof(gw->ip_addr));
+     gw->port = lgtd_sockaddrport(peer);
+     memcpy(gw->site.as_array, site, sizeof(gw->site.as_array));
++    if (peer->ss_family == AF_INET) {
++        bool ok = lgtd_lifx_gateway_resolve_ipv4_addr(
++            gw->socket, (const struct sockaddr_in *)peer, gw->addr
++        );
++        if (!ok) {
++            lgtd_warn("couldn't resolve %s to a LIFX address", gw->ip_addr);
++        }
++    }
++
+     gw->last_req_at = received_at;
+     gw->next_req_at = received_at;
+     gw->last_pkt_at = received_at;
+@@ -336,7 +370,7 @@
+ 
+     // In case this is the first bulb (re-)discovered, start the watchdog, it
+     // will stop by itself:
+-    lgtd_lifx_timer_start_watchdog();
++    lgtd_lifx_timer_start_timers();
+ 
+     LGTD_STATS_ADD_AND_UPDATE_PROCTITLE(gateways, 1);
+ 
+@@ -489,12 +523,8 @@
          (uintmax_t)pkt->tags
      );
  
@@ -432,7 +540,7 @@
  
      assert(sizeof(*pkt) == sizeof(b->state));
      lgtd_lifx_bulb_set_light_state(
-@@ -519,7 +515,7 @@
+@@ -519,7 +549,7 @@
          }
      }
  
@@ -441,7 +549,7 @@
      if (latency < LGTD_LIFX_GATEWAY_MIN_REFRESH_INTERVAL_MSECS) {
          if (!event_pending(gw->refresh_ev, EV_TIMEOUT, NULL)) {
              int timeout = LGTD_LIFX_GATEWAY_MIN_REFRESH_INTERVAL_MSECS - latency;
-@@ -559,14 +555,9 @@
+@@ -559,14 +589,9 @@
          gw->ip_addr, gw->port, lgtd_addrtoa(hdr->target.device_addr), pkt->power
      );
  
@@ -458,7 +566,7 @@
  }
  
  int
-@@ -673,9 +664,10 @@
+@@ -673,9 +698,10 @@
      }
  }
  
@@ -472,7 +580,7 @@
  {
      assert(gw && hdr && pkt);
  
-@@ -685,12 +677,8 @@
+@@ -685,12 +711,8 @@
          (uintmax_t)pkt->tags
      );
  
@@ -487,7 +595,7 @@
  
      int tag_id;
      LGTD_LIFX_WIRE_FOREACH_TAG_ID(tag_id, pkt->tags) {
-@@ -707,3 +695,129 @@
+@@ -707,3 +729,129 @@
  
      lgtd_lifx_bulb_set_tags(b, pkt->tags);
  }
@@ -620,7 +728,7 @@
 diff --git a/lifx/gateway.h b/lifx/gateway.h
 --- a/lifx/gateway.h
 +++ b/lifx/gateway.h
-@@ -36,6 +36,12 @@
+@@ -36,12 +36,19 @@
  struct lgtd_lifx_gateway {
      LIST_ENTRY(lgtd_lifx_gateway)   link;
      struct lgtd_lifx_bulb_list      bulbs;
@@ -633,7 +741,14 @@
      // Multiple gateways can share the same site (that happens when bulbs are
      // far away enough that ZigBee can't be used). Moreover the SET_PAN_GATEWAY
      // packet doesn't include the device address in the header (i.e: site and
-@@ -60,6 +66,7 @@
+     // device_addr have the same value) so we have no choice but to use the
+     // remote ip address to identify a gateway:
+     struct sockaddr_storage         peer;
++    uint8_t                         addr[LGTD_LIFX_ADDR_LENGTH];
+     char                            ip_addr[INET6_ADDRSTRLEN];
+     uint16_t                        port;
+     // TODO: just use an integer and rename it to site_id:
+@@ -60,6 +67,7 @@
      lgtd_time_mono_t                last_req_at;
      lgtd_time_mono_t                next_req_at;
      lgtd_time_mono_t                last_pkt_at;
@@ -641,7 +756,7 @@
      struct lgtd_lifx_message        pkt_ring[LGTD_LIFX_GATEWAY_PACKET_RING_SIZE];
  #define LGTD_LIFX_GATEWAY_INC_MESSAGE_RING_INDEX(idx)  do { \
      (idx) += 1;                                             \
-@@ -77,6 +84,12 @@
+@@ -77,6 +85,12 @@
  
  extern struct lgtd_lifx_gateway_list lgtd_lifx_gateways;
  
@@ -654,7 +769,7 @@
  struct lgtd_lifx_gateway *lgtd_lifx_gateway_get(const struct sockaddr_storage *);
  struct lgtd_lifx_gateway *lgtd_lifx_gateway_open(const struct sockaddr_storage *,
                                                   ev_socklen_t,
-@@ -119,3 +132,15 @@
+@@ -119,3 +133,15 @@
  void lgtd_lifx_gateway_handle_tags(struct lgtd_lifx_gateway *,
                                     const struct lgtd_lifx_packet_header *,
                                     const struct lgtd_lifx_packet_tags *);
@@ -670,6 +785,185 @@
 +void lgtd_lifx_gateway_handle_runtime_info(struct lgtd_lifx_gateway *,
 +                                           const struct lgtd_lifx_packet_header *,
 +                                           const struct lgtd_lifx_packet_runtime_info *);
+diff --git a/lifx/timer.c b/lifx/timer.c
+--- a/lifx/timer.c
++++ b/lifx/timer.c
+@@ -38,9 +38,11 @@
+ static struct {
+     struct event *watchdog_interval_ev;
+     struct event *discovery_timeout_ev;
+-} lgtd_lifx_timer_context = {
++    struct event *metadata_refresh_ev;
++} lgtd_lifx_timers = {
+     .watchdog_interval_ev = NULL,
+-    .discovery_timeout_ev = NULL
++    .discovery_timeout_ev = NULL,
++    .metadata_refresh_ev = NULL
+ };
+ 
+ static void
+@@ -64,7 +66,7 @@
+     }
+ 
+     struct timeval tv = LGTD_MSECS_TO_TIMEVAL(timeout);
+-    if (event_add(lgtd_lifx_timer_context.discovery_timeout_ev, &tv)
++    if (event_add(lgtd_lifx_timers.discovery_timeout_ev, &tv)
+         || !lgtd_lifx_broadcast_discovery()) {
+         lgtd_err(1, "can't start discovery");
+     }
+@@ -132,29 +134,46 @@
+     }
+ }
+ 
++static void
++lgtd_lifx_timer_metadata_refresh_event_callback(evutil_socket_t socket,
++                                                short events,
++                                                void *ctx)
++{
++    (void)socket;
++    (void)events;
++    (void)ctx;
++
++    struct lgtd_lifx_gateway *gw, *next_gw;
++    LIST_FOREACH_SAFE(gw, &lgtd_lifx_gateways, link, next_gw) {
++        lgtd_lifx_gateway_send_to_site(gw, LGTD_LIFX_GET_MESH_INFO, NULL);
++        lgtd_lifx_gateway_send_to_site(gw, LGTD_LIFX_GET_WIFI_INFO, NULL);
++        lgtd_lifx_gateway_send_to_site(gw, LGTD_LIFX_GET_INFO, NULL);
++    }
++}
++
+ bool
+ lgtd_lifx_timer_setup(void)
+ {
+-    assert(!lgtd_lifx_timer_context.watchdog_interval_ev);
+-    assert(!lgtd_lifx_timer_context.discovery_timeout_ev);
++    assert(!lgtd_lifx_timers.watchdog_interval_ev);
++    assert(!lgtd_lifx_timers.discovery_timeout_ev);
++    assert(!lgtd_lifx_timers.metadata_refresh_ev);
+ 
+-    lgtd_lifx_timer_context.discovery_timeout_ev = event_new(
+-        lgtd_ev_base,
+-        -1,
+-        0,
+-        lgtd_lifx_timer_discovery_timeout_event_callback,
+-        NULL
++    lgtd_lifx_timers.discovery_timeout_ev = event_new(
++        lgtd_ev_base, -1, 0,
++        lgtd_lifx_timer_discovery_timeout_event_callback, NULL
+     );
+-    lgtd_lifx_timer_context.watchdog_interval_ev = event_new(
+-        lgtd_ev_base,
+-        -1,
+-        EV_PERSIST,
+-        lgtd_lifx_timer_watchdog_timeout_event_callback,
+-        NULL
++    lgtd_lifx_timers.watchdog_interval_ev = event_new(
++        lgtd_ev_base, -1, EV_PERSIST,
++        lgtd_lifx_timer_watchdog_timeout_event_callback, NULL
++    );
++    lgtd_lifx_timers.metadata_refresh_ev = event_new(
++        lgtd_ev_base, -1, EV_PERSIST,
++        lgtd_lifx_timer_metadata_refresh_event_callback, NULL
+     );
+ 
+-    if (lgtd_lifx_timer_context.discovery_timeout_ev
+-        && lgtd_lifx_timer_context.watchdog_interval_ev) {
++    if (lgtd_lifx_timers.discovery_timeout_ev
++        && lgtd_lifx_timers.watchdog_interval_ev
++        && lgtd_lifx_timers.metadata_refresh_ev) {
+         return true;
+     }
+ 
+@@ -167,42 +186,53 @@
+ void
+ lgtd_lifx_timer_close(void)
+ {
+-    if (lgtd_lifx_timer_context.discovery_timeout_ev) {
+-        event_del(lgtd_lifx_timer_context.discovery_timeout_ev);
+-        event_free(lgtd_lifx_timer_context.discovery_timeout_ev);
+-        lgtd_lifx_timer_context.discovery_timeout_ev = NULL;
++    if (lgtd_lifx_timers.discovery_timeout_ev) {
++        event_del(lgtd_lifx_timers.discovery_timeout_ev);
++        event_free(lgtd_lifx_timers.discovery_timeout_ev);
++        lgtd_lifx_timers.discovery_timeout_ev = NULL;
+     }
+-    if (lgtd_lifx_timer_context.watchdog_interval_ev) {
+-        event_del(lgtd_lifx_timer_context.watchdog_interval_ev);
+-        event_free(lgtd_lifx_timer_context.watchdog_interval_ev);
+-        lgtd_lifx_timer_context.watchdog_interval_ev = NULL;
++    if (lgtd_lifx_timers.watchdog_interval_ev) {
++        event_del(lgtd_lifx_timers.watchdog_interval_ev);
++        event_free(lgtd_lifx_timers.watchdog_interval_ev);
++        lgtd_lifx_timers.watchdog_interval_ev = NULL;
++    }
++    if (lgtd_lifx_timers.metadata_refresh_ev) {
++        event_del(lgtd_lifx_timers.metadata_refresh_ev);
++        event_free(lgtd_lifx_timers.metadata_refresh_ev);
++        lgtd_lifx_timers.metadata_refresh_ev = NULL;
+     }
+ }
+ 
+ void
+-lgtd_lifx_timer_start_watchdog(void)
++lgtd_lifx_timer_start_timers(void)
+ {
+     assert(
+         !RB_EMPTY(&lgtd_lifx_bulbs_table) || !LIST_EMPTY(&lgtd_lifx_gateways)
+     );
+ 
+-    if (!evtimer_pending(lgtd_lifx_timer_context.watchdog_interval_ev, NULL)) {
++    if (!evtimer_pending(lgtd_lifx_timers.watchdog_interval_ev, NULL)) {
+         struct timeval tv = LGTD_MSECS_TO_TIMEVAL(
+             LGTD_LIFX_TIMER_WATCHDOG_INTERVAL_MSECS
+         );
+-        if (event_add(lgtd_lifx_timer_context.watchdog_interval_ev, &tv)) {
+-            lgtd_err(1, "can't start watchdog");
++        if (event_add(lgtd_lifx_timers.watchdog_interval_ev, &tv)) {
++            lgtd_err(1, "can't start timers");
+         }
+-        lgtd_debug("starting watchdog timer");
+     }
++    if (!evtimer_pending(lgtd_lifx_timers.metadata_refresh_ev, NULL)) {
++        struct timeval tv = {
++            .tv_sec = LGTD_LIFX_TIMER_METADATA_REFRESH_INTERVAL_SECS
++        };
++        if (event_add(lgtd_lifx_timers.metadata_refresh_ev, &tv)) {
++            lgtd_err(1, "can't start timers");
++        }
++    }
++    lgtd_debug("starting timers");
+ }
+ 
+ void
+ lgtd_lifx_timer_start_discovery(void)
+ {
+-    assert(!evtimer_pending(
+-        lgtd_lifx_timer_context.discovery_timeout_ev, NULL
+-    ));
++    assert(!evtimer_pending(lgtd_lifx_timers.discovery_timeout_ev, NULL));
+ 
+     lgtd_lifx_timer_discovery_timeout_event_callback(-1, 0, NULL);
+     lgtd_debug("starting discovery timer");
+diff --git a/lifx/timer.h b/lifx/timer.h
+--- a/lifx/timer.h
++++ b/lifx/timer.h
+@@ -17,9 +17,13 @@
+ 
+ #pragma once
+ 
+-enum { LGTD_LIFX_TIMER_WATCHDOG_INTERVAL_MSECS = 500 };
+-enum { LGTD_LIFX_TIMER_ACTIVE_DISCOVERY_INTERVAL_MSECS = 2000 };
+-enum { LGTD_LIFX_TIMER_PASSIVE_DISCOVERY_INTERVAL_MSECS = 10000 };
++enum lgtd_lifx_timer_intervals {
++    LGTD_LIFX_TIMER_WATCHDOG_INTERVAL_MSECS = 500,
++    LGTD_LIFX_TIMER_ACTIVE_DISCOVERY_INTERVAL_MSECS = 2000,
++    LGTD_LIFX_TIMER_PASSIVE_DISCOVERY_INTERVAL_MSECS = 10000,
++    LGTD_LIFX_TIMER_METADATA_REFRESH_INTERVAL_SECS = 60
++};
++
+ enum { LGTD_LIFX_TIMER_DEVICE_TIMEOUT_MSECS = 3000 };
+ enum { LGTD_LIFX_TIMER_DEVICE_FORCE_REFRESH_MSECS = 2000 };
+ 
 diff --git a/lifx/wire_proto.c b/lifx/wire_proto.c
 --- a/lifx/wire_proto.c
 +++ b/lifx/wire_proto.c
--- a/series	Thu Aug 13 02:29:45 2015 -0700
+++ b/series	Fri Aug 14 02:31:38 2015 -0700
@@ -1,3 +1,4 @@
 use_jq_when_avalaible.patch
 fix_ipv4_address_formatting.patch
+fix_command_pipe_read_loop.patch
 implement_some_metadata_packet_types.patch