Improve the UBus support in DNSMASQ:

- Aligned the handling of UBus connections with the DBus code as it makes it a 
bit easier
to comprehend and allow for automatic reconnects when the connection to UBus 
drops;
- make sure that DNSMASQ can connect to UBus when running as another user than 
root;
- added status checks and logging to the various UBus calls to aid debugging 
from an
enduser point of view;
- show the (lack of) support for UBus in the configuration string.

---
 src/config.h  |   4 ++
 src/dnsmasq.c |  38 ++++++++++++++++-
 src/dnsmasq.h |   6 +++
 src/ubus.c    | 114 ++++++++++++++++++++++++++++++++++++++++----------
 4 files changed, 139 insertions(+), 23 deletions(-)

diff --git a/src/config.h b/src/config.h
index 203d69e..99b22eb 100644
--- a/src/config.h
+++ b/src/config.h
@@ -362,6 +362,10 @@ static char *compile_opts =
 "no-"
 #endif
 "DBus "
+#ifndef HAVE_UBUS
+"no-"
+#endif
+"UBus "
 #ifndef LOCALEDIR
 "no-"
 #endif
diff --git a/src/dnsmasq.c b/src/dnsmasq.c
index 3f3edbd..5d89aa1 100644
--- a/src/dnsmasq.c
+++ b/src/dnsmasq.c
@@ -420,6 +420,18 @@ int main (int argc, char **argv)
   die(_("DBus not available: set HAVE_DBUS in src/config.h"), NULL, 
EC_BADCONF);
 #endif
 
+  if (option_bool(OPT_UBUS))
+#ifdef HAVE_UBUS
+    {
+      const char *err;
+      daemon->ubus = NULL;
+      if ((err = ubus_init()))
+        my_syslog(LOG_WARNING, _("UBus init failed: %s"), (char *)err, 
EC_MISC);
+    }
+#else
+  die(_("UBus not available: set HAVE_UBUS in src/config.h"), NULL, 
EC_BADCONF);
+#endif
+
   if (daemon->port != 0)
     pre_allocate_sfds();
 
@@ -812,6 +824,16 @@ int main (int argc, char **argv)
     }
 #endif
 
+#ifdef HAVE_UBUS
+  if (option_bool(OPT_UBUS))
+    {
+      if (daemon->ubus)
+        my_syslog(LOG_INFO, _("UBus support enabled: connected to system 
bus"));
+      else
+        my_syslog(LOG_INFO, _("UBus support enabled: bus connection pending"));
+    }
+#endif
+
 #ifdef HAVE_DNSSEC
   if (option_bool(OPT_DNSSEC_VALID))
     {
@@ -1000,7 +1022,7 @@ int main (int argc, char **argv)
 
 #ifdef HAVE_UBUS
       if (option_bool(OPT_UBUS))
-         set_ubus_listeners();
+        set_ubus_listeners();
 #endif
          
 #ifdef HAVE_DHCP
@@ -1135,7 +1157,19 @@ int main (int argc, char **argv)
 
 #ifdef HAVE_UBUS
       if (option_bool(OPT_UBUS))
-        check_ubus_listeners();
+        {
+          /* if we didn't create a UBus connection, retry now. */
+          if (!daemon->ubus)
+            {
+              const char *err;
+              if ((err = ubus_init()))
+                my_syslog(LOG_WARNING, _("UBus problem: %s"), err);
+              if (daemon->ubus)
+                my_syslog(LOG_INFO, _("Connected to system UBus"));
+            }
+
+          check_ubus_listeners();
+        }
 #endif
 
       check_dns_listeners(now);
diff --git a/src/dnsmasq.h b/src/dnsmasq.h
index 0e409b4..f5e154e 100644
--- a/src/dnsmasq.h
+++ b/src/dnsmasq.h
@@ -1119,6 +1119,11 @@ extern struct daemon {
 #ifdef HAVE_DBUS
   struct watch *watches;
 #endif
+  /* UBus stuff */
+#ifdef HAVE_UBUS
+  /* void * here to avoid depending on ubus headers outside ubus.c */
+  void *ubus;
+#endif
 
   /* TFTP stuff */
   struct tftp_transfer *tftp_trans, *tftp_done_trans;
@@ -1456,6 +1461,7 @@ void emit_dbus_signal(int action, struct dhcp_lease 
*lease, char
*hostname);
 
 /* ubus.c */
 #ifdef HAVE_UBUS
+const char *ubus_init(void);
 void set_ubus_listeners(void);
 void check_ubus_listeners(void);
 void ubus_event_bcast(const char *type, const char *mac, const char *ip, const 
char
*name, const char *interface);
diff --git a/src/ubus.c b/src/ubus.c
index 703a60d..c58c231 100644
--- a/src/ubus.c
+++ b/src/ubus.c
@@ -20,29 +20,94 @@
 
 #include <libubus.h>
 
-static struct ubus_context *ubus = NULL;
 static struct blob_buf b;
+static int notify;
 
 static int ubus_handle_metrics(struct ubus_context *ctx, struct ubus_object 
*obj,
                               struct ubus_request_data *req, const char 
*method,
                               struct blob_attr *msg);
-static struct ubus_method ubus_object_methods[] = {
-  {.name = "metrics", .handler = ubus_handle_metrics},
+
+static void ubus_subscribe_cb(struct ubus_context *ctx, struct ubus_object 
*obj);
+
+static const struct ubus_method ubus_object_methods[] = {
+  UBUS_METHOD_NOARG("metrics", ubus_handle_metrics),
 };
 
-static struct ubus_object_type ubus_object_type = UBUS_OBJECT_TYPE("dnsmasq",
ubus_object_methods);
+static struct ubus_object_type ubus_object_type =
+  UBUS_OBJECT_TYPE("dnsmasq", ubus_object_methods);
 
 static struct ubus_object ubus_object = {
   .name = "dnsmasq",
   .type = &ubus_object_type,
   .methods = ubus_object_methods,
   .n_methods = ARRAY_SIZE(ubus_object_methods),
+  .subscribe_cb = ubus_subscribe_cb,
 };
 
+static void ubus_subscribe_cb(struct ubus_context *ctx, struct ubus_object 
*obj)
+{
+  (void)ctx;
+
+  my_syslog(LOG_DEBUG, _("UBus subscription callback: %s subscriber(s)"), obj-
>has_subscribers ? "1" : "0");
+  notify = obj->has_subscribers;
+}
+
+void ubus_destroy(struct ubus_context *ubus)
+{
+  // Forces re-initialization when we're reusing the same definitions later on.
+  ubus_object.id = 0;
+  ubus_object_type.id = 0;
+
+  ubus_free(ubus);
+  daemon->ubus = NULL;
+}
+
+static void ubus_disconnect_cb(struct ubus_context *ubus)
+{
+  int ret;
+
+  ret = ubus_reconnect(ubus, NULL);
+  if (ret)
+    {
+      my_syslog(LOG_ERR, _("Failed to reconnect to UBus: %s"), 
ubus_strerror(ret));
+
+      ubus_destroy(ubus);
+    }
+}
+
+const char *ubus_init()
+{
+  struct ubus_context *ubus = NULL;
+  int ret;
+
+  ubus = ubus_connect(NULL);
+  if (!ubus)
+    {
+      ret = UBUS_STATUS_CONNECTION_FAILED;
+      return ubus_strerror(ret);
+    }
+
+  ubus->connection_lost = ubus_disconnect_cb;
+
+  ret = ubus_add_object(ubus, &ubus_object);
+  if (ret)
+    {
+      return ubus_strerror(ret);
+    }
+
+  daemon->ubus = ubus;
+
+  return NULL;
+}
+
 void set_ubus_listeners()
 {
+  struct ubus_context *ubus = (struct ubus_context *)daemon->ubus;
   if (!ubus)
-    return;
+    {
+      my_syslog(LOG_ERR, _("Cannot set UBus listeners: no connection"));
+      return;
+    }
 
   poll_listen(ubus->sock.fd, POLLIN);
   poll_listen(ubus->sock.fd, POLLERR);
@@ -51,46 +116,51 @@ void set_ubus_listeners()
 
 void check_ubus_listeners()
 {
+  struct ubus_context *ubus = (struct ubus_context *)daemon->ubus;
   if (!ubus)
     {
-      ubus = ubus_connect(NULL);
-      if (!ubus)
-       return;
-      ubus_add_object(ubus, &ubus_object);
+      my_syslog(LOG_ERR, _("Cannot poll UBus listeners: no connection"));
+      return;
     }
   
   if (poll_check(ubus->sock.fd, POLLIN))
     ubus_handle_event(ubus);
   
-  if (poll_check(ubus->sock.fd, POLLHUP))
+  if (poll_check(ubus->sock.fd, POLLHUP | POLLERR))
     {
-      ubus_free(ubus);
-      ubus = NULL;
+      my_syslog(LOG_INFO, _("Disconnecting from UBus"));
+
+      ubus_destroy(ubus);
     }
 }
 
-
 static int ubus_handle_metrics(struct ubus_context *ctx, struct ubus_object 
*obj,
                               struct ubus_request_data *req, const char 
*method,
                               struct blob_attr *msg)
 {
   int i;
-  blob_buf_init(&b, 0);
 
-  for(i=0; i < __METRIC_MAX; i++)
+  (void)obj;
+  (void)method;
+  (void)msg;
+
+  blob_buf_init(&b, BLOBMSG_TYPE_TABLE);
+
+  for (i=0; i < __METRIC_MAX; i++)
     blobmsg_add_u32(&b, get_metric_name(i), daemon->metrics[i]);
   
-  ubus_send_reply(ctx, req, b.head);
-  
-  return 0;
+  return ubus_send_reply(ctx, req, b.head);
 }
 
 void ubus_event_bcast(const char *type, const char *mac, const char *ip, const 
char
*name, const char *interface)
 {
-  if (!ubus || !ubus_object.has_subscribers)
+  struct ubus_context *ubus = (struct ubus_context *)daemon->ubus;
+  int ret;
+
+  if (!ubus || !notify)
     return;
 
-  blob_buf_init(&b, 0);
+  blob_buf_init(&b, BLOBMSG_TYPE_TABLE);
   if (mac)
     blobmsg_add_string(&b, "mac", mac);
   if (ip)
@@ -100,7 +170,9 @@ void ubus_event_bcast(const char *type, const char *mac, 
const char
*ip, const c
   if (interface)
     blobmsg_add_string(&b, "interface", interface);
   
-  ubus_notify(ubus, &ubus_object, type, b.head, -1);
+  ret = ubus_notify(ubus, &ubus_object, type, b.head, -1);
+  if (!ret)
+    my_syslog(LOG_ERR, _("Failed to send UBus event: %s"), ubus_strerror(ret));
 }

-- 
2.20.1



_______________________________________________
Dnsmasq-discuss mailing list
Dnsmasq-discuss@lists.thekelleys.org.uk
http://lists.thekelleys.org.uk/mailman/listinfo/dnsmasq-discuss

Reply via email to