From 1500ad07f703b9cf51b38e16be35b2d5250a4015 Mon Sep 17 00:00:00 2001
From: Anjan Chanda <anjan.chanda@iopsys.eu>
Date: Thu, 9 Jun 2022 10:55:57 +0200
Subject: [PATCH] libmapbus.so: from generic bus.h, implement for ubus

---
 src/Makefile     |   9 +-
 src/bus.h        |  53 ++++++++
 src/cntlr.c      |   3 +-
 src/cntlr_map.c  |   1 +
 src/cntlr_ubus.c | 292 +-----------------------------------------
 src/cntlr_ubus.h |  24 ----
 src/ubus.c       | 320 +++++++++++++++++++++++++++++++++++++++++++++++
 7 files changed, 385 insertions(+), 317 deletions(-)
 create mode 100644 src/bus.h
 create mode 100644 src/ubus.c

diff --git a/src/Makefile b/src/Makefile
index b045b998..a3a995bd 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -2,6 +2,8 @@ EXECS = mapcontroller
 CFLAGS+=-I. -Iutils -D_GNU_SOURCE
 CFLAGS+= -g -Wall -Werror
 
+LIBOBJS = ubus.o
+
 OBJS = \
 	utils/debug.o \
 	utils/liblist.o \
@@ -34,14 +36,17 @@ HOOKS = pre-commit
 
 .PHONY: all check clean plugins FORCE
 
-all: $(EXECS) plugins
+all: libmapbus.so $(EXECS) plugins
 
 
 %.o: %.c
 	$(CC) $(CFLAGS) -c -o $@ $<
 
 mapcontroller: $(OBJS)
-	$(CC) $(LDFLAGS) -o $@ $^ $(LIBS)
+	$(CC) $(LDFLAGS) -o $@ $^ $(LIBS) -L. -lmapbus
+
+libmapbus.so: $(LIBOBJS)
+	$(CC) -shared -Wl,-soname,libmapbus.so $^ -o $@ -lblobmsg_json -lubus
 
 plugins:
 	@echo "$(plugin_subdirs)"
diff --git a/src/bus.h b/src/bus.h
new file mode 100644
index 00000000..aeedc3ae
--- /dev/null
+++ b/src/bus.h
@@ -0,0 +1,53 @@
+/*
+ * bus.h - ipc abstraction header
+ *
+ * Copyright (C) 2019-2022 IOPSYS Software Solutions AB. All rights reserved.
+ *
+ * Author: anjan.chanda@iopsys.eu
+ *
+ */
+
+#ifndef BUS_H
+#define BUS_H
+
+extern int bus_init(void *userdata, void *cfgopts, void **bus);
+extern void bus_exit(void *userdata, void *bus);
+
+typedef int (*op_t)(void *data, size_t len);
+
+struct bus_object_op {
+	const char *name;
+	op_t op;
+};
+
+struct bus_object {
+	const char *name;
+	size_t num_ops;
+	struct bus_object_op *ops;
+};
+
+extern int bus_add_object(void *bus, struct bus_object o);
+extern void bus_del_object(void *bus, const char *objname);
+
+extern int bus_wait_for_object_timeout(void *bus, void *object, uint32_t tmo_msecs, void *res);
+
+extern int bus_call_object(void *bus, void *object, void *function,
+			   void *request, size_t len,
+			   void (*cb)(void *userdata, char *response, size_t len),
+			   void *userdata, uint32_t timeout);
+
+extern int bus_register_object_notifier(void *bus, void *object,
+					void (*cb)(void *userdata, void *event, void *extra),
+					void *userdata, void **notifier);
+
+extern void bus_unregister_notifier(void *bus, void *notifier);
+
+extern int bus_register_event_notifier(void *bus, void *event,
+				       void (*cb)(void *userdata, void *event, void *extra),
+				       void *userdata, void **notifier);
+
+extern void bus_notify_event(void *bus, void *event, void *extra);
+
+extern char *bus_strerror(int num, char *string, size_t len);
+
+#endif /* BUS_H */
diff --git a/src/cntlr.c b/src/cntlr.c
index 976471e2..729527af 100644
--- a/src/cntlr.c
+++ b/src/cntlr.c
@@ -40,7 +40,8 @@
 #include "config.h"
 #include "cntlr.h"
 #include "allsta.h"
-#include "cntlr_ubus.h"
+#include "cntlr_ubus.h"		/* deprecate */
+#include "bus.h"
 #include "cntlr_map.h"
 #include "cntlr_cmdu.h"
 #include "steer_module.h"
diff --git a/src/cntlr_map.c b/src/cntlr_map.c
index 61f86348..c5f86732 100644
--- a/src/cntlr_map.c
+++ b/src/cntlr_map.c
@@ -45,6 +45,7 @@
 #include "config.h"
 #include "cntlr.h"
 #include "cntlr_ubus.h"
+#include "bus.h"
 
 #include "cntlr_map_debug.h"
 #include "cntlr_cmdu.h"
diff --git a/src/cntlr_ubus.c b/src/cntlr_ubus.c
index 8df2e53f..809985a4 100644
--- a/src/cntlr_ubus.c
+++ b/src/cntlr_ubus.c
@@ -39,7 +39,8 @@
 #include "cntlr.h"
 #include "allsta.h"
 #include "cntlr_map.h"
-#include "cntlr_ubus.h"
+#include "cntlr_ubus.h"		/* deprecate */
+#include "bus.h"
 #include "cntlr_tlv.h"
 
 #include "cntlr_tlv.h"
@@ -2905,19 +2906,6 @@ static int cntlr_cac_term(struct ubus_context *ctx, struct ubus_object *obj,
 	return UBUS_STATUS_OK;
 }
 
-void bus_notify_event(void *bus, void *event, void *extra)
-{
-	struct blob_buf b;
-
-	memset(&b, 0, sizeof(struct blob_buf));
-	blob_buf_init(&b, 0);
-	if (extra)
-		blobmsg_add_json_from_string(&b, (char *)extra);
-
-	ubus_send_event(bus, (char *)event, b.head);
-	blob_buf_free(&b);
-}
-
 int cntlr_comb_metrics(struct ubus_context *ctx,
 		struct ubus_object *obj, struct ubus_request_data *req,
 		const char *method, struct blob_attr *msg)
@@ -3154,42 +3142,6 @@ out:
 	return ret;
 }
 
-/* tmo_msecs: 0 = immediate */
-int bus_wait_for_object_timeout(void *bus, void *object, uint32_t tmo_msecs,
-				void *res)
-{
-	uint32_t tmo = tmo_msecs * 1000;
-	uint32_t dur = 0;
-	uint32_t obj;
-
-
-	*((uint32_t *)res) = 0;
-	do {
-		int ret;
-
-		usleep(dur);
-		ret = ubus_lookup_id(bus, (char *)object, &obj);
-		if (!ret) {
-			*((uint32_t *)res) = obj;
-			return 0;
-		}
-
-		if (!tmo)
-			return -1;
-
-		if (tmo >= 1000000) {
-			dur = 1000000;
-			tmo -= 1000000;
-		} else {
-			dur = tmo;
-			tmo = 0;
-		}
-
-		trace("%s not up, retry after %u ms\n", (char *)object, dur / 1000);
-	} while (dur > 0);
-
-	return -1;
-}
 
 static void ieee1905_cb_get_almac(void *userdata, char *resp, size_t len)
 {
@@ -3230,243 +3182,3 @@ int cntlr_get_ieee1905_almac(struct controller *c, uint8_t *almac)
 
 	return ret;
 }
-
-char *bus_strerror(int num, char *string, size_t len)
-{
-	if (!string || !len)
-		return (char *)ubus_strerror(num);
-
-	return strncpy(string, ubus_strerror(num), len);
-}
-
-struct ubus_notifier {
-	char *event;
-	void *priv;
-	void (*user_cb)(void *priv, void *event, void *extra);
-	struct ubus_event_handler evh;
-	void (*ubus_cb)(struct ubus_context *ctx,
-			 struct ubus_event_handler *ev,
-			 const char *type,
-			 struct blob_attr *m);
-};
-
-static void ubus_object_cb(struct ubus_context *ctx, struct ubus_event_handler *ev,
-			   const char *type, struct blob_attr *msg)
-{
-	struct ubus_notifier *n = container_of(ev, struct ubus_notifier, evh);
-	char *str;
-
-	str = blobmsg_format_json(msg, true);
-	if (!str) {
-		dbg("%s: received event not json formatted!\n", __func__);
-		return;
-	}
-	free(str);
-
-	if (!strcmp(type, "ubus.object.add") || !strcmp(type, "ubus.object.remove")) {
-		struct blob_attr *tb[2];
-		char path[64] = {0};
-		uint32_t id = 0;
-		static const struct blobmsg_policy attrs[2] = {
-			[0] = { .name = "id", .type = BLOBMSG_TYPE_INT32 },
-			[1] = { .name = "path", .type = BLOBMSG_TYPE_STRING }
-		};
-
-		blobmsg_parse(attrs, 2, tb, blob_data(msg), blob_len(msg));
-		if (!tb[0] || !tb[1])
-			return;
-
-		strncpy(path, blobmsg_data(tb[1]), sizeof(path) - 1);
-		id = (uint32_t) blobmsg_get_u32(tb[0]);
-		UNUSED(id);
-
-		if (!strncmp(path, n->event, strlen(n->event))) {
-			if (n->user_cb) {
-				if (!strcmp(type, "ubus.object.add"))
-					n->user_cb(n->priv, "OBJECT_AVAILABLE", NULL);
-				else
-					n->user_cb(n->priv, "OBJECT_UNAVAILABLE", NULL);
-			}
-		}
-	}
-}
-
-int bus_register_object_notifier(void *bus, void *object,
-				 void (*cb)(void *userdata, void *event, void *extra),
-				 void *userdata, void **notifier)
-{
-	struct ubus_notifier *t = calloc(1, sizeof(*t));
-	int ret;
-
-	if (!t)
-		return -1;
-
-	t->evh.cb = ubus_object_cb;
-	t->event = strdup((char *)object);
-	t->priv = userdata;
-	t->user_cb = cb;
-
-	ret = ubus_register_event_handler(bus, &t->evh, "ubus.object.*");
-	if (ret) {
-		dbg("%s: err = %s\n", __func__, ubus_strerror(ret));
-		free(t);
-	}
-
-	*notifier = ret ? NULL : t;
-	return ret;
-}
-
-void bus_unregister_notifier(void *bus, void *notifier)
-{
-	struct ubus_notifier *t = (struct ubus_notifier *)notifier;
-
-	if (!bus || !notifier)
-		return;
-
-	ubus_unregister_event_handler(bus, &t->evh);
-	free(t->event);
-	free(t);
-}
-
-static void ubus_event_cb(struct ubus_context *ctx, struct ubus_event_handler *ev,
-			  const char *type, struct blob_attr *msg)
-{
-	struct ubus_notifier *n = container_of(ev, struct ubus_notifier, evh);
-	char *str;
-
-	str = blobmsg_format_json(msg, true);
-	if (!str) {
-		dbg("%s: received event not json formatted!\n", __func__);
-		return;
-	}
-
-	if (!strncmp(type, n->event, strlen(n->event))) {
-		struct blob_attr *tb[2];
-		char path[64] = {0};
-		uint32_t id = 0;
-		static const struct blobmsg_policy attrs[2] = {
-			[0] = { .name = "id", .type = BLOBMSG_TYPE_INT32 },
-			[1] = { .name = "path", .type = BLOBMSG_TYPE_STRING }
-		};
-
-		blobmsg_parse(attrs, 2, tb, blob_data(msg), blob_len(msg));
-		if (!tb[0] || !tb[1])
-			return;
-
-		strncpy(path, blobmsg_data(tb[1]), sizeof(path) - 1);
-		id = (uint32_t) blobmsg_get_u32(tb[0]);
-		UNUSED(id);
-
-		if (!strncmp(path, n->event, strlen(n->event))) {
-			if (n->user_cb)
-				n->user_cb(n->priv, n->event, str);
-		}
-	}
-
-	free(str);
-}
-
-int bus_register_event_notifier(void *bus, void *event,
-				 void (*cb)(void *userdata, void *event, void *extra),
-				 void *userdata, void **notifier)
-{
-	struct ubus_notifier *t = calloc(1, sizeof(*t));
-	int ret;
-
-	if (!t)
-		return -1;
-
-	t->ubus_cb = ubus_event_cb;
-	t->evh.cb = t->ubus_cb;
-	t->event = strdup((char *)event);
-	t->priv = userdata;
-	t->user_cb = cb;
-
-	ret = ubus_register_event_handler(bus, &t->evh, t->event);
-	if (ret) {
-		dbg("%s: err = %s\n", __func__, ubus_strerror(ret));
-		free(t);
-	}
-
-	*notifier = ret ? NULL : t;
-	return ret;
-}
-
-struct ubus_caller_context {
-	void *data;
-	void (*cb)(void *data, char *resp, size_t len);
-};
-
-static void ubus_call_object_cb(struct ubus_request *r, int type, struct blob_attr *msg)
-{
-	struct ubus_caller_context *uc = r->priv;
-	struct json_object *jobj = NULL;
-	char *str;
-
-	if (!msg || !uc) {
-		err("%s: msg or passed context is NULL\n", __func__);
-		return;
-	}
-
-	str = blobmsg_format_json(msg, true);
-	if (str) {
-		jobj = json_tokener_parse(str);
-		if (jobj) {
-			json_object_put(jobj);
-			if (uc->cb)
-				uc->cb(uc->data, str, strlen(str) + 1);
-		}
-		free(str);
-	}
-}
-
-int bus_call_object(void *bus, void *object, void *function,
-		    void *request, size_t len,
-		    void (*cb)(void *userdata, char *response, size_t len),
-		    void *userdata, uint32_t timeout)
-{
-	struct blob_buf bb = {0};
-	struct ubus_caller_context *uc;
-	int ret;
-
-
-	if (!bus || !object || !function)
-		return -1;
-
-	uc = calloc(1, sizeof(*uc));
-	if (!uc)
-		return -1;
-
-	uc->data = userdata;
-	uc->cb = cb;
-
-	blob_buf_init(&bb, 0);
-	if (len && request && ((char *)request)[len] == '\0') {
-		bool res;
-
-		res = blobmsg_add_json_from_string(&bb, request);
-		if (!res) {
-			err("%s: 'invalid request format'\n", __func__);
-			free(uc);
-			return -1;
-		}
-	}
-
-	ret = ubus_invoke(bus, *(uint32_t *)object, (char *)function, bb.head,
-			  ubus_call_object_cb, uc, timeout);
-	if (ret) {
-		char *str;
-
-		err("%s: err = '%s'(%d)\n", __func__, ubus_strerror(ret), ret);
-		str = blobmsg_format_json(bb.head, true);
-		if (str) {
-			fprintf(stderr, "%s: arg: '%s'\n\n\n", __func__, str);
-			free(str);
-		}
-	}
-
-	blob_buf_free(&bb);
-	free(uc);
-	return ret;
-}
-
diff --git a/src/cntlr_ubus.h b/src/cntlr_ubus.h
index 2276054f..34d2210b 100644
--- a/src/cntlr_ubus.h
+++ b/src/cntlr_ubus.h
@@ -10,37 +10,13 @@
 #ifndef CNTLR_UBUS_H
 #define CNTLR_UBUS_H
 
-void bus_notify_event(void *bus, void *event, void *extra);
 
 extern int cntlr_publish_object(struct controller *c, const char *objname);
 extern void cntlr_remove_object(struct controller *c);
 extern int cntlr_register_module(struct controller *c);
 
-
-int bus_call_object(void *bus, void *object, void *function,
-		    void *request, size_t len,
-		    void (*cb)(void *userdata, char *response, size_t len),
-		    void *userdata, uint32_t timeout);
-
-
-int bus_wait_for_object_timeout(void *bus, void *object, uint32_t tmo_msecs, void *res);
-
 int ieee1905_buildcmdu_linkmetric_resp(struct controller *c, uint16_t msg_type);
 
 int cntlr_get_ieee1905_almac(struct controller *c, uint8_t *almac);
 
-
-int bus_register_object_notifier(void *bus, void *object,
-				 void (*cb)(void *userdata, void *event, void *extra),
-				 void *userdata, void **notifier);
-
-void bus_unregister_notifier(void *bus, void *notifier);
-
-
-int bus_register_event_notifier(void *bus, void *event,
-				 void (*cb)(void *userdata, void *event, void *extra),
-				 void *userdata, void **notifier);
-
-char *bus_strerror(int num, char *string, size_t len);
-
 #endif /* CNTLR_UBUS_H */
diff --git a/src/ubus.c b/src/ubus.c
new file mode 100644
index 00000000..d7f58ac3
--- /dev/null
+++ b/src/ubus.c
@@ -0,0 +1,320 @@
+/*
+ * ubus.c - ubus implementation of bus apis
+ *
+ * Copyright (C) 2019-2022 IOPSYS Software Solutions AB. All rights reserved.
+ *
+ * Author: anjan.chanda@iopsys.eu
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include <libubox/blobmsg.h>
+#include <libubox/blobmsg_json.h>
+#include <libubox/uloop.h>
+#include <libubox/ustream.h>
+#include <libubox/utils.h>
+#include <libubus.h>
+
+#include <easy/easy.h>
+#include <timer_impl.h>
+
+#include "utils/utils.h"
+#include "utils/debug.h"
+#include "bus.h"
+
+void bus_notify_event(void *bus, void *event, void *extra)
+{
+	struct blob_buf b;
+
+	memset(&b, 0, sizeof(struct blob_buf));
+	blob_buf_init(&b, 0);
+	if (extra)
+		blobmsg_add_json_from_string(&b, (char *)extra);
+
+	ubus_send_event(bus, (char *)event, b.head);
+	blob_buf_free(&b);
+}
+
+/* tmo_msecs: 0 = immediate, i.e. one lookup, no retry */
+int bus_wait_for_object_timeout(void *bus, void *object, uint32_t tmo_msecs,
+				void *res)
+{
+	uint32_t tmo = tmo_msecs * 1000;
+	uint32_t dur = 0;
+	uint32_t obj;
+
+
+	*((uint32_t *)res) = 0;
+	do {
+		int ret;
+
+		usleep(dur);
+		ret = ubus_lookup_id(bus, (char *)object, &obj);
+		if (!ret) {
+			*((uint32_t *)res) = obj;
+			return 0;
+		}
+
+		if (!tmo)
+			return -1;
+
+		if (tmo >= 1000000) {
+			dur = 1000000;
+			tmo -= 1000000;
+		} else {
+			dur = tmo;
+			tmo = 0;
+		}
+
+		trace("%s not up, retry after %u ms\n", (char *)object, dur / 1000);
+	} while (dur > 0);
+
+	return -1;
+}
+
+char *bus_strerror(int num, char *string, size_t len)
+{
+	if (!string || !len)
+		return (char *)ubus_strerror(num);
+
+	return strncpy(string, ubus_strerror(num), len);
+}
+
+struct ubus_notifier {
+	char *event;
+	void *priv;
+	void (*user_cb)(void *priv, void *event, void *extra);
+	struct ubus_event_handler evh;
+	void (*ubus_cb)(struct ubus_context *ctx,
+			 struct ubus_event_handler *ev,
+			 const char *type,
+			 struct blob_attr *m);
+};
+
+static void ubus_object_cb(struct ubus_context *ctx, struct ubus_event_handler *ev,
+			   const char *type, struct blob_attr *msg)
+{
+	struct ubus_notifier *n = container_of(ev, struct ubus_notifier, evh);
+	char *str;
+
+	str = blobmsg_format_json(msg, true);
+	if (!str) {
+		dbg("%s: received event not json formatted!\n", __func__);
+		return;
+	}
+	free(str);
+
+	if (!strcmp(type, "ubus.object.add") || !strcmp(type, "ubus.object.remove")) {
+		struct blob_attr *tb[2];
+		char path[64] = {0};
+		uint32_t id = 0;
+		static const struct blobmsg_policy attrs[2] = {
+			[0] = { .name = "id", .type = BLOBMSG_TYPE_INT32 },
+			[1] = { .name = "path", .type = BLOBMSG_TYPE_STRING }
+		};
+
+		blobmsg_parse(attrs, 2, tb, blob_data(msg), blob_len(msg));
+		if (!tb[0] || !tb[1])
+			return;
+
+		strncpy(path, blobmsg_data(tb[1]), sizeof(path) - 1);
+		id = (uint32_t) blobmsg_get_u32(tb[0]);
+		UNUSED(id);
+
+		if (!strncmp(path, n->event, strlen(n->event))) {
+			if (n->user_cb) {
+				if (!strcmp(type, "ubus.object.add"))
+					n->user_cb(n->priv, "OBJECT_AVAILABLE", NULL);
+				else
+					n->user_cb(n->priv, "OBJECT_UNAVAILABLE", NULL);
+			}
+		}
+	}
+}
+
+int bus_register_object_notifier(void *bus, void *object,
+				 void (*cb)(void *userdata, void *event, void *extra),
+				 void *userdata, void **notifier)
+{
+	struct ubus_notifier *t = calloc(1, sizeof(*t));
+	int ret;
+
+	if (!t)
+		return -1;
+
+	t->evh.cb = ubus_object_cb;
+	t->event = strdup((char *)object);
+	t->priv = userdata;
+	t->user_cb = cb;
+
+	ret = ubus_register_event_handler(bus, &t->evh, "ubus.object.*");
+	if (ret) {
+		dbg("%s: err = %s\n", __func__, ubus_strerror(ret));
+		free(t);
+	}
+
+	*notifier = ret ? NULL : t;
+	return ret;
+}
+
+void bus_unregister_notifier(void *bus, void *notifier)
+{
+	struct ubus_notifier *t = (struct ubus_notifier *)notifier;
+
+	if (!bus || !notifier)
+		return;
+
+	ubus_unregister_event_handler(bus, &t->evh);
+	free(t->event);
+	free(t);
+}
+
+static void ubus_event_cb(struct ubus_context *ctx, struct ubus_event_handler *ev,
+			  const char *type, struct blob_attr *msg)
+{
+	struct ubus_notifier *n = container_of(ev, struct ubus_notifier, evh);
+	char *str;
+
+	str = blobmsg_format_json(msg, true);
+	if (!str) {
+		dbg("%s: received event not json formatted!\n", __func__);
+		return;
+	}
+
+	if (!strncmp(type, n->event, strlen(n->event))) {
+		struct blob_attr *tb[2];
+		char path[64] = {0};
+		uint32_t id = 0;
+		static const struct blobmsg_policy attrs[2] = {
+			[0] = { .name = "id", .type = BLOBMSG_TYPE_INT32 },
+			[1] = { .name = "path", .type = BLOBMSG_TYPE_STRING }
+		};
+
+		blobmsg_parse(attrs, 2, tb, blob_data(msg), blob_len(msg));
+		if (!tb[0] || !tb[1])
+			return;
+
+		strncpy(path, blobmsg_data(tb[1]), sizeof(path) - 1);
+		id = (uint32_t) blobmsg_get_u32(tb[0]);
+		UNUSED(id);
+
+		if (!strncmp(path, n->event, strlen(n->event))) {
+			if (n->user_cb)
+				n->user_cb(n->priv, n->event, str);
+		}
+	}
+
+	free(str);
+}
+
+int bus_register_event_notifier(void *bus, void *event,
+				 void (*cb)(void *userdata, void *event, void *extra),
+				 void *userdata, void **notifier)
+{
+	struct ubus_notifier *t = calloc(1, sizeof(*t));
+	int ret;
+
+	if (!t)
+		return -1;
+
+	t->ubus_cb = ubus_event_cb;
+	t->evh.cb = t->ubus_cb;
+	t->event = strdup((char *)event);
+	t->priv = userdata;
+	t->user_cb = cb;
+
+	ret = ubus_register_event_handler(bus, &t->evh, t->event);
+	if (ret) {
+		dbg("%s: err = %s\n", __func__, ubus_strerror(ret));
+		free(t);
+	}
+
+	*notifier = ret ? NULL : t;
+	return ret;
+}
+
+struct ubus_caller_context {
+	void *data;
+	void (*cb)(void *data, char *resp, size_t len);
+};
+
+static void ubus_call_object_cb(struct ubus_request *r, int type, struct blob_attr *msg)
+{
+	struct ubus_caller_context *uc = r->priv;
+	struct json_object *jobj = NULL;
+	char *str;
+
+	if (!msg || !uc) {
+		err("%s: msg or passed context is NULL\n", __func__);
+		return;
+	}
+
+	str = blobmsg_format_json(msg, true);
+	if (str) {
+		jobj = json_tokener_parse(str);
+		if (jobj) {
+			json_object_put(jobj);
+			if (uc->cb)
+				uc->cb(uc->data, str, strlen(str) + 1);
+		}
+		free(str);
+	}
+}
+
+int bus_call_object(void *bus, void *object, void *function,
+		    void *request, size_t len,
+		    void (*cb)(void *userdata, char *response, size_t len),
+		    void *userdata, uint32_t timeout)
+{
+	struct blob_buf bb = {0};
+	struct ubus_caller_context *uc;
+	int ret;
+
+
+	if (!bus || !object || !function)
+		return -1;
+
+	uc = calloc(1, sizeof(*uc));
+	if (!uc)
+		return -1;
+
+	uc->data = userdata;
+	uc->cb = cb;
+
+	blob_buf_init(&bb, 0);
+	if (len && request && ((char *)request)[len] == '\0') {
+		bool res;
+
+		res = blobmsg_add_json_from_string(&bb, request);
+		if (!res) {
+			err("%s: 'invalid request format'\n", __func__);
+			free(uc);
+			return -1;
+		}
+	}
+
+	ret = ubus_invoke(bus, *(uint32_t *)object, (char *)function, bb.head,
+			  ubus_call_object_cb, uc, timeout);
+	if (ret) {
+		char *str;
+
+		err("%s: err = '%s'(%d)\n", __func__, ubus_strerror(ret), ret);
+		str = blobmsg_format_json(bb.head, true);
+		if (str) {
+			fprintf(stderr, "%s: arg: '%s'\n\n\n", __func__, str);
+			free(str);
+		}
+	}
+
+	blob_buf_free(&bb);
+	free(uc);
+	return ret;
+}
+
-- 
GitLab