Improved UBus supported

- aligned the handling of UBus connections with the DBus code as it
makes it a bit easier to comprehend;
- added logging to the various UBus calls to aid debugging from an
enduser point of view, but be careful to not flood the logs;
- show the (lack of) support for UBus in the configuration string.
diff --git a/src/ubus.c b/src/ubus.c
index 703a60d..c2cca22 100644
--- a/src/ubus.c
+++ b/src/ubus.c
@@ -20,29 +20,112 @@
 
 #include <libubus.h>
 
-static struct ubus_context *ubus = NULL;
 static struct blob_buf b;
+static int notify;
+static int error_logged = 0;
 
 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;
+}
+
+static 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, _("Cannot reconnect to UBus: %s"), ubus_strerror(ret));
+
+      ubus_destroy(ubus);
+    }
+}
+
+void ubus_init()
+{
+  struct ubus_context *ubus = NULL;
+  int ret = 0;
+
+  ubus = ubus_connect(NULL);
+  if (!ubus)
+    {
+      if (!error_logged)
+        {
+          my_syslog(LOG_ERR, _("Cannot initialize UBus: connection failed"));
+          error_logged = 1;
+        }
+
+      ubus_destroy(ubus);
+      return;
+    }
+
+  ret = ubus_add_object(ubus, &ubus_object);
+  if (ret)
+    {
+      if (!error_logged)
+        {
+          my_syslog(LOG_ERR, _("Cannot add object to UBus: %s"), ubus_strerror(ret));
+          error_logged = 1;
+        }
+      return;
+    }
+
+  ubus->connection_lost = ubus_disconnect_cb;
+  daemon->ubus = ubus;
+  error_logged = 0;
+
+  my_syslog(LOG_INFO, _("Connected to system UBus"));
+}
+
 void set_ubus_listeners()
 {
+  struct ubus_context *ubus = (struct ubus_context *)daemon->ubus;
   if (!ubus)
-    return;
+    {
+      if (!error_logged)
+        {
+          my_syslog(LOG_ERR, _("Cannot set UBus listeners: no connection"));
+          error_logged = 1;
+        }
+      return;
+    }
+
+  error_logged = 0;
 
   poll_listen(ubus->sock.fd, POLLIN);
   poll_listen(ubus->sock.fd, POLLERR);
@@ -51,46 +134,57 @@
 
 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);
+      if (!error_logged)
+        {
+          my_syslog(LOG_ERR, _("Cannot poll UBus listeners: no connection"));
+          error_logged = 1;
+        }
+      return;
     }
   
+  error_logged = 0;
+
   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 +194,9 @@
   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));
 }