--- /dev/null
+Makefile
+CMakeCache.txt
+CMakeFiles
+*.cmake
+*.a
+ubus.sock
+listener
+ubusd
+ubus
--- /dev/null
+cmake_minimum_required(VERSION 2.8)
+
+PROJECT(ubus C)
+ADD_DEFINITIONS(-Os -Wall -Werror --std=gnu99 -g3)
+ADD_LIBRARY(ubus STATIC libubus.c)
+
+ADD_EXECUTABLE(ubusd ubusd.c ubusd_id.c ubusd_obj.c ubusd_proto.c)
+TARGET_LINK_LIBRARIES(ubusd ubox)
+
+ADD_EXECUTABLE(cli cli.c)
+SET_TARGET_PROPERTIES(cli PROPERTIES OUTPUT_NAME ubus)
+TARGET_LINK_LIBRARIES(cli ubus ubox)
+
+ADD_EXECUTABLE(listener listener.c)
+TARGET_LINK_LIBRARIES(listener ubus ubox)
+
--- /dev/null
+#include "libubus.h"
+
+static struct blob_buf b;
+static struct ubus_context *ctx;
+
+static void receive_lookup(struct ubus_request *req, int type, struct blob_attr *msg)
+{
+ struct blob_attr **attr, *cur;
+ char *s;
+ int rem;
+
+ attr = ubus_parse_msg(msg);
+ if (!attr[UBUS_ATTR_OBJID] || !attr[UBUS_ATTR_OBJPATH])
+ return;
+
+ fprintf(stderr, "'%s' @%08x\n",
+ (char *) blob_data(attr[UBUS_ATTR_OBJPATH]),
+ blob_get_int32(attr[UBUS_ATTR_OBJID]));
+
+ if (!attr[UBUS_ATTR_SIGNATURE])
+ return;
+
+ blob_for_each_attr(cur, attr[UBUS_ATTR_SIGNATURE], rem) {
+ s = blobmsg_format_json(cur, false);
+ fprintf(stderr, "\t%s\n", s);
+ free(s);
+ }
+}
+
+static int usage(char *prog)
+{
+ fprintf(stderr,
+ "Usage: %s <command> [arguments...]\n"
+ "Commands:\n"
+ " - list [<path>] List objects\n"
+ "\n", prog);
+ return 1;
+}
+
+int main(int argc, char **argv)
+{
+ struct ubus_request req;
+ char *cmd;
+ int ret;
+
+ ctx = ubus_connect(NULL);
+ if (!ctx) {
+ fprintf(stderr, "Failed to connect to ubus\n");
+ return -1;
+ }
+
+ cmd = argv[1];
+ if (argc < 2)
+ return usage(argv[0]);
+
+ blob_buf_init(&b, 0);
+
+ if (!strcmp(cmd, "list")) {
+ if (argc == 3)
+ blob_put_string(&b, UBUS_ATTR_OBJPATH, argv[2]);
+
+ ubus_start_request(ctx, &req, b.head, UBUS_MSG_LOOKUP, 0);
+ req.data_cb = receive_lookup;
+ ret = ubus_complete_request(ctx, &req);
+ if (ret)
+ fprintf(stderr, "Failed: %d\n", ret);
+ } else {
+ return usage(argv[0]);
+ }
+
+ ubus_free(ctx);
+ return 0;
+}
--- /dev/null
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <sys/socket.h>
+#include <unistd.h>
+
+#include <libubox/blob.h>
+#include <libubox/blobmsg.h>
+#include <libubox/usock.h>
+
+#include "libubus.h"
+#include "ubusmsg.h"
+
+#define DEBUG 1
+
+#ifdef DEBUG
+#define DPRINTF(_format, ...) fprintf(stderr, "ubus: " _format, ## __VA_ARGS__)
+#else
+#define DPRINTF(...) do {} while(0)
+#endif
+
+#define STATIC_IOV(_var) { .iov_base = (char *) &(_var), .iov_len = sizeof(_var) }
+
+const char *__ubus_strerror[__UBUS_STATUS_LAST] = {
+ [UBUS_STATUS_OK] = "Success",
+ [UBUS_STATUS_INVALID_COMMAND] = "Invalid command",
+ [UBUS_STATUS_INVALID_ARGUMENT] = "Invalid argument",
+ [UBUS_STATUS_NOT_FOUND] = "Not found",
+ [UBUS_STATUS_NO_DATA] = "No response",
+};
+
+static struct blob_buf b;
+
+static const struct blob_attr_info ubus_policy[UBUS_ATTR_MAX] = {
+ [UBUS_ATTR_STATUS] = { .type = BLOB_ATTR_INT32 },
+ [UBUS_ATTR_OBJID] = { .type = BLOB_ATTR_INT32 },
+ [UBUS_ATTR_OBJPATH] = { .type = BLOB_ATTR_STRING },
+};
+static struct blob_attr *attrbuf[UBUS_ATTR_MAX];
+
+struct ubus_pending_data {
+ struct list_head list;
+ int type;
+ struct blob_attr data[];
+};
+
+struct blob_attr **ubus_parse_msg(struct blob_attr *msg)
+{
+ blob_parse(msg, attrbuf, ubus_policy, UBUS_ATTR_MAX);
+ return attrbuf;
+}
+
+const char *ubus_strerror(int error)
+{
+ static char err[32];
+
+ if (error < 0 || error >= __UBUS_STATUS_LAST)
+ goto out;
+
+ if (!__ubus_strerror[error])
+ goto out;
+
+ return __ubus_strerror[error];
+
+out:
+ sprintf(err, "Unknown error: %d", error);
+ return err;
+}
+
+int ubus_start_request(struct ubus_context *ctx, struct ubus_request *req,
+ struct blob_attr *msg, int cmd, uint32_t peer)
+{
+ struct ubus_msghdr hdr;
+ struct iovec iov[2] = {
+ STATIC_IOV(hdr)
+ };
+
+ memset(req, 0, sizeof(*req));
+ hdr.version = 0;
+ hdr.type = cmd;
+ hdr.seq = ++ctx->request_seq;
+ hdr.peer = peer;
+
+ req->peer = hdr.peer;
+ req->seq = hdr.seq;
+
+ if (!msg) {
+ blob_buf_init(&b, 0);
+ msg = b.head;
+ }
+
+ iov[1].iov_base = (char *) msg;
+ iov[1].iov_len = blob_raw_len(msg);
+ INIT_LIST_HEAD(&req->list);
+ INIT_LIST_HEAD(&req->pending);
+
+ return writev(ctx->sock.fd, iov, 2);
+}
+
+static bool recv_retry(int fd, struct iovec *iov, bool wait)
+{
+ int bytes;
+
+ while (iov->iov_len > 0) {
+ bytes = read(fd, iov->iov_base, iov->iov_len);
+ if (bytes < 0) {
+ bytes = 0;
+ if (errno == EINTR)
+ continue;
+
+ if (errno != EAGAIN) {
+ perror("read");
+ return false;
+ }
+ }
+ if (!wait && !bytes)
+ return false;
+
+ wait = true;
+ iov->iov_len -= bytes;
+ iov->iov_base += bytes;
+ }
+
+ return true;
+}
+
+static bool ubus_validate_hdr(struct ubus_msghdr *hdr)
+{
+ if (hdr->version != 0)
+ return false;
+
+ if (blob_raw_len(hdr->data) < sizeof(*hdr->data))
+ return false;
+
+ if (blob_raw_len(hdr->data) + sizeof(*hdr) > UBUS_MAX_MSGLEN)
+ return false;
+
+ return true;
+}
+
+static bool get_next_msg(struct ubus_context *ctx, bool wait)
+{
+ struct iovec iov = STATIC_IOV(ctx->msgbuf.hdr);
+
+ /* receive header + start attribute */
+ iov.iov_len += sizeof(struct blob_attr);
+ if (!recv_retry(ctx->sock.fd, &iov, wait))
+ return false;
+
+ iov.iov_len = blob_len(ctx->msgbuf.hdr.data);
+ if (iov.iov_len > 0 && !recv_retry(ctx->sock.fd, &iov, true))
+ return false;
+
+ return ubus_validate_hdr(&ctx->msgbuf.hdr);
+}
+
+static bool ubus_get_status(struct ubus_msghdr *hdr, int *ret)
+{
+ ubus_parse_msg(hdr->data);
+
+ if (!attrbuf[UBUS_ATTR_STATUS])
+ return false;
+
+ *ret = blob_get_int32(attrbuf[UBUS_ATTR_STATUS]);
+ return true;
+}
+
+static void ubus_process_req_data(struct ubus_request *req)
+{
+ struct ubus_pending_data *data;
+
+ while (!list_empty(&req->pending)) {
+ data = list_first_entry(&req->pending,
+ struct ubus_pending_data, list);
+ list_del(&data->list);
+ req->data_cb(req, data->type, data->data);
+ free(data);
+ }
+}
+
+static void ubus_req_complete_cb(struct ubus_request *req)
+{
+ ubus_complete_handler_t cb = req->complete_cb;
+
+ if (!cb)
+ return;
+
+ req->complete_cb = NULL;
+ cb(req, req->status_code);
+}
+
+static int ubus_process_req_status(struct ubus_request *req, struct ubus_msghdr *hdr)
+{
+ int ret = UBUS_STATUS_INVALID_ARGUMENT;
+
+ if (!list_empty(&req->list))
+ list_del(&req->list);
+
+ ubus_get_status(hdr, &ret);
+ req->peer = hdr->peer;
+ req->status_msg = true;
+ req->status_code = ret;
+ if (!req->blocked)
+ ubus_req_complete_cb(req);
+
+ return ret;
+}
+
+static void ubus_req_data(struct ubus_request *req, struct ubus_msghdr *hdr)
+{
+ struct ubus_pending_data *data;
+ int len;
+
+ if (!req->blocked) {
+ req->blocked = true;
+ req->data_cb(req, hdr->type, hdr->data);
+ ubus_process_req_data(req);
+ req->blocked = false;
+
+ if (req->status_msg)
+ ubus_req_complete_cb(req);
+
+ return;
+ }
+
+ len = blob_raw_len(hdr->data);
+ data = calloc(1, sizeof(*data) + len);
+ if (!data)
+ return;
+
+ data->type = hdr->type;
+ memcpy(data->data, hdr->data, len);
+ list_add(&data->list, &req->pending);
+}
+
+static void ubus_process_msg(struct ubus_context *ctx, struct ubus_msghdr *hdr)
+{
+ struct ubus_request *req;
+
+ list_for_each_entry(req, &ctx->requests, list) {
+ if (hdr->seq != req->seq || hdr->peer != req->peer)
+ continue;
+
+ switch(hdr->type) {
+ case UBUS_MSG_STATUS:
+ ubus_process_req_status(req, hdr);
+ return;
+ case UBUS_MSG_DATA:
+ if (req->data_cb)
+ ubus_req_data(req, hdr);
+ break;
+ default:
+ DPRINTF("unknown message type: %d\n", hdr->type);
+ break;
+ }
+ }
+}
+
+void ubus_abort_request(struct ubus_context *ctx, struct ubus_request *req)
+{
+ if (!list_empty(&req->list))
+ return;
+
+ list_del(&req->list);
+}
+
+void ubus_complete_request_async(struct ubus_context *ctx, struct ubus_request *req)
+{
+ if (!list_empty(&req->list))
+ return;
+
+ list_add(&req->list, &ctx->requests);
+}
+
+static void ubus_handle_data(struct uloop_fd *u, unsigned int events)
+{
+ struct ubus_context *ctx = container_of(u, struct ubus_context, sock);
+ struct ubus_msghdr *hdr = &ctx->msgbuf.hdr;
+
+ while (get_next_msg(ctx, false))
+ ubus_process_msg(ctx, hdr);
+}
+
+int ubus_complete_request(struct ubus_context *ctx, struct ubus_request *req)
+{
+ struct ubus_msghdr *hdr = &ctx->msgbuf.hdr;
+
+ if (!list_empty(&req->list))
+ list_del(&req->list);
+
+ while (1) {
+ if (req->status_msg)
+ return req->status_code;
+
+ if (!get_next_msg(ctx, true))
+ return UBUS_STATUS_NO_DATA;
+
+ if (hdr->seq != req->seq || hdr->peer != req->peer)
+ goto skip;
+
+ switch(hdr->type) {
+ case UBUS_MSG_STATUS:
+ return ubus_process_req_status(req, hdr);
+ case UBUS_MSG_DATA:
+ if (req->data_cb)
+ ubus_req_data(req, hdr);
+ continue;
+ default:
+ DPRINTF("unknown message type: %d\n", hdr->type);
+ continue;
+ }
+
+skip:
+ ubus_process_msg(ctx, hdr);
+ }
+}
+
+void ubus_invoke_async(struct ubus_context *ctx, uint32_t obj, const char *method,
+ struct blob_attr *msg, struct ubus_request *req)
+{
+ blob_buf_init(&b, 0);
+ blob_put_int32(&b, UBUS_ATTR_OBJID, obj);
+ blob_put_string(&b, UBUS_ATTR_METHOD, method);
+ blob_put(&b, UBUS_ATTR_DATA, blob_data(msg), blob_len(msg));
+
+ ubus_start_request(ctx, req, b.head, UBUS_MSG_INVOKE, obj);
+}
+
+int ubus_invoke(struct ubus_context *ctx, uint32_t obj, const char *method,
+ struct blob_attr *msg, ubus_data_handler_t cb, void *priv)
+{
+ struct ubus_request req;
+
+ ubus_invoke_async(ctx, obj, method, msg, &req);
+ req.data_cb = cb;
+ req.priv = priv;
+ return ubus_complete_request(ctx, &req);
+}
+
+static void ubus_publish_cb(struct ubus_request *req, int type, struct blob_attr *msg)
+{
+ struct ubus_object *obj = req->priv;
+
+ ubus_parse_msg(msg);
+
+ if (!attrbuf[UBUS_ATTR_OBJID])
+ return;
+
+ obj->id = blob_get_int32(attrbuf[UBUS_ATTR_OBJID]);
+
+ if (attrbuf[UBUS_ATTR_OBJTYPE])
+ obj->type->id = blob_get_int32(attrbuf[UBUS_ATTR_OBJTYPE]);
+}
+
+static bool ubus_push_table_data(const struct ubus_signature **sig, int *rem, bool array)
+{
+ const struct ubus_signature *cur;
+ bool nest_type;
+ void *nest;
+
+ while (rem) {
+ cur = (*sig)++;
+ (*rem)--;
+ switch(cur->type) {
+ case UBUS_SIGNATURE_END:
+ return !array;
+ case BLOBMSG_TYPE_INT32:
+ case BLOBMSG_TYPE_STRING:
+ blobmsg_add_u32(&b, cur->name, cur->type);
+ break;
+ case BLOBMSG_TYPE_TABLE:
+ case BLOBMSG_TYPE_ARRAY:
+ nest_type = cur->type == BLOBMSG_TYPE_ARRAY;
+ nest = blobmsg_open_nested(&b, cur->name, nest_type);
+ if (!ubus_push_table_data(sig, rem, nest_type))
+ return false;
+ blobmsg_close_table(&b, nest);
+ break;
+ default:
+ return false;
+ }
+ if (array)
+ return true;
+ }
+ return false;
+}
+
+static bool ubus_push_object_type(struct ubus_object_type *type)
+{
+ void *s, *m;
+ int rem = type->n_signature;
+ const struct ubus_signature *sig = type->signature;
+
+ s = blob_nest_start(&b, UBUS_ATTR_SIGNATURE);
+ while (rem) {
+ if (sig->type != UBUS_SIGNATURE_METHOD)
+ return false;
+
+ m = blobmsg_open_table(&b, sig->name);
+
+ sig++;
+ rem--;
+ if (!ubus_push_table_data(&sig, &rem, false))
+ return false;
+
+ blobmsg_close_table(&b, m);
+ }
+ blob_nest_end(&b, s);
+
+ return true;
+}
+
+int ubus_publish(struct ubus_context *ctx, struct ubus_object *obj)
+{
+ struct ubus_request req;
+ int ret;
+
+ if (obj->id || !obj->name || !obj->type)
+ return UBUS_STATUS_INVALID_ARGUMENT;
+
+ blob_buf_init(&b, 0);
+ blob_put_string(&b, UBUS_ATTR_OBJPATH, obj->name);
+ if (obj->parent)
+ blob_put_int32(&b, UBUS_ATTR_OBJID, obj->parent->id);
+
+ if (obj->type->id)
+ blob_put_int32(&b, UBUS_ATTR_OBJTYPE, obj->type->id);
+ else if (!ubus_push_object_type(obj->type))
+ return UBUS_STATUS_INVALID_ARGUMENT;
+
+ ubus_start_request(ctx, &req, b.head, UBUS_MSG_PUBLISH, 0);
+ req.data_cb = ubus_publish_cb;
+ req.priv = obj;
+ ret = ubus_complete_request(ctx, &req);
+ if (ret)
+ return ret;
+
+ if (!obj->id)
+ return UBUS_STATUS_NO_DATA;
+
+ return 0;
+}
+
+struct ubus_context *ubus_connect(const char *path)
+{
+ struct ubus_context *ctx;
+ struct {
+ struct ubus_msghdr hdr;
+ struct blob_attr data;
+ } hdr;
+ struct blob_attr *buf;
+
+ if (!path)
+ path = UBUS_UNIX_SOCKET;
+
+ ctx = calloc(1, sizeof(*ctx));
+ if (!ctx)
+ goto error;
+
+ ctx->sock.fd = usock(USOCK_UNIX, path, NULL);
+ if (ctx->sock.fd < 0) {
+ DPRINTF("Failed to connect to server\n");
+ goto error_free;
+ }
+ ctx->sock.cb = ubus_handle_data;
+
+ if (read(ctx->sock.fd, &hdr, sizeof(hdr)) != sizeof(hdr)) {
+ DPRINTF("Failed to read initial message data\n");
+ goto error_close;
+ }
+
+ if (!ubus_validate_hdr(&hdr.hdr)) {
+ DPRINTF("Failed to validate initial message header\n");
+ goto error_close;
+ }
+
+ if (hdr.hdr.type != UBUS_MSG_HELLO) {
+ DPRINTF("Unexpected initial message\n");
+ goto error_close;
+ }
+
+ buf = calloc(1, blob_raw_len(&hdr.data));
+ if (!buf)
+ goto error_close;
+
+ memcpy(buf, &hdr.data, sizeof(hdr.data));
+ if (read(ctx->sock.fd, blob_data(buf), blob_len(buf)) != blob_len(buf)) {
+ DPRINTF("Failed to retrieve initial message data\n");
+ goto error_free_buf;
+ }
+
+ ctx->local_id = hdr.hdr.peer;
+ free(buf);
+
+ if (!ctx->local_id) {
+ DPRINTF("Failed to get local peer id\n");
+ goto error_close;
+ }
+
+ return ctx;
+
+error_free_buf:
+ free(buf);
+error_close:
+ close(ctx->sock.fd);
+error_free:
+ free(ctx);
+error:
+ return NULL;
+}
+
+void ubus_free(struct ubus_context *ctx)
+{
+ close(ctx->sock.fd);
+ free(ctx);
+}
--- /dev/null
+#include <libubox/list.h>
+#include <libubox/blobmsg.h>
+#include <libubox/uloop.h>
+#include <stdint.h>
+#include "ubusmsg.h"
+#include "ubus_common.h"
+
+struct ubus_msg_src;
+struct ubus_object;
+struct ubus_request;
+struct ubus_request_data;
+
+typedef void (*ubus_handler_t)(struct ubus_object *obj,
+ struct ubus_request_data *req,
+ const char *method, struct blob_attr *msg);
+typedef void (*ubus_data_handler_t)(struct ubus_request *req,
+ int type, struct blob_attr *msg);
+typedef void (*ubus_complete_handler_t)(struct ubus_request *req, int ret);
+
+
+#define UBUS_SIGNATURE(_type, _name) { .type = _type, .name = _name }
+
+#define UBUS_METHOD_START(_name) UBUS_SIGNATURE(UBUS_SIGNATURE_METHOD, _name)
+#define UBUS_METHOD_END() UBUS_SIGNATURE(UBUS_SIGNATURE_END, NULL)
+
+#define UBUS_FIELD(_type, _name) UBUS_SIGNATURE(BLOBMSG_TYPE_ ## _type, _name)
+
+#define UBUS_ARRAY(_name) UBUS_FIELD(ARRAY, _name)
+#define UBUS_ARRAY_END() UBUS_SIGNATURE(UBUS_SIGNATURE_END, NULL)
+
+#define UBUS_TABLE_START(_name) UBUS_FIELD(TABLE, _name)
+#define UBUS_TABLE_END() UBUS_SIGNATURE(UBUS_SIGNATURE_END, NULL)
+
+#define UBUS_OBJECT_TYPE(_name, _signature) \
+ { \
+ .name = _name, \
+ .id = 0, \
+ .n_signature = ARRAY_SIZE(_signature), \
+ .signature = _signature \
+ }
+
+struct ubus_signature {
+ enum blobmsg_type type;
+ const char *name;
+};
+
+struct ubus_object_type {
+ const char *name;
+ uint32_t id;
+ int n_signature;
+ const struct ubus_signature *signature;
+};
+
+struct ubus_object {
+ const char *name;
+ uint32_t id;
+
+ const char *path;
+ struct ubus_object *parent;
+
+ struct ubus_object_type *type;
+};
+
+struct ubus_context {
+ struct list_head requests;
+ struct list_head objects;
+ struct uloop_fd sock;
+
+ uint32_t local_id;
+ uint32_t request_seq;
+
+ struct {
+ struct ubus_msghdr hdr;
+ char data[UBUS_MAX_MSGLEN - sizeof(struct ubus_msghdr)];
+ } msgbuf;
+};
+
+struct ubus_request_data {
+ uint32_t object;
+ uint32_t peer;
+ uint32_t seq;
+};
+
+struct ubus_request {
+ struct list_head list;
+
+ struct list_head pending;
+ bool status_msg;
+ int status_code;
+ bool blocked;
+
+ uint32_t peer;
+ uint32_t seq;
+
+ ubus_data_handler_t data_cb;
+ ubus_complete_handler_t complete_cb;
+
+ void *priv;
+};
+
+#define BLOBMSG_END_TABLE BLOBMSG_TYPE_UNSPEC
+
+struct ubus_context *ubus_connect(const char *path);
+void ubus_free(struct ubus_context *ctx);
+
+const char *ubus_strerror(int error);
+
+/* ----------- helpers for message handling ----------- */
+
+struct blob_attr **ubus_parse_msg(struct blob_attr *msg);
+
+/* ----------- raw request handling ----------- */
+
+/* start a raw request */
+int ubus_start_request(struct ubus_context *ctx, struct ubus_request *req,
+ struct blob_attr *msg, int cmd, uint32_t peer);
+
+/* wait for a request to complete and return its status */
+int ubus_complete_request(struct ubus_context *ctx, struct ubus_request *req);
+
+/* complete a request asynchronously */
+void ubus_complete_request_async(struct ubus_context *ctx,
+ struct ubus_request *req);
+
+/* abort an asynchronous request */
+void ubus_abort_request(struct ubus_context *ctx, struct ubus_request *req);
+
+/* ----------- rpc ----------- */
+
+/* invoke a method on a specific object */
+int ubus_invoke(struct ubus_context *ctx, uint32_t obj, const char *method,
+ struct blob_attr *msg, ubus_data_handler_t cb, void *priv);
+
+/* asynchronous version of ubus_invoke() */
+void ubus_invoke_async(struct ubus_context *ctx, uint32_t obj, const char *method,
+ struct blob_attr *msg, struct ubus_request *req);
+
+/* make an object visible to remote connections */
+int ubus_publish(struct ubus_context *ctx, struct ubus_object *obj);
+
+
--- /dev/null
+#include "libubus.h"
+
+static struct ubus_context *ctx;
+
+static const struct ubus_signature test_object_sig[] = {
+ UBUS_METHOD_START("hello"),
+ UBUS_ARRAY("test"),
+ UBUS_TABLE_START(NULL),
+ UBUS_FIELD(INT32, "id"),
+ UBUS_FIELD(STRING, "msg"),
+ UBUS_TABLE_END(),
+ UBUS_METHOD_END(),
+};
+
+static struct ubus_object_type test_object_type =
+ UBUS_OBJECT_TYPE("test", test_object_sig);
+
+static struct ubus_object test_object = {
+ .name = "test",
+ .type = &test_object_type,
+};
+
+static struct ubus_object test_object2 = {
+ .name = "test2",
+ .type = &test_object_type,
+};
+
+int main(int argc, char **argv)
+{
+ int ret;
+
+ ctx = ubus_connect(NULL);
+ if (!ctx) {
+ fprintf(stderr, "Failed to connect to ubus\n");
+ return -1;
+ }
+
+ fprintf(stderr, "Connected as ID 0x%08x\n", ctx->local_id);
+
+ fprintf(stderr, "Publishing object\n");
+ ret = ubus_publish(ctx, &test_object);
+ if (ret)
+ fprintf(stderr, "Failed to publish object: %s\n", ubus_strerror(ret));
+ else {
+ fprintf(stderr, "Object ID: %08x\n", test_object.id);
+ fprintf(stderr, "Object Type ID: %08x\n", test_object.type->id);
+ }
+
+ fprintf(stderr, "Publishing object\n");
+ ret = ubus_publish(ctx, &test_object2);
+ if (ret)
+ fprintf(stderr, "Failed to publish object: %s\n", ubus_strerror(ret));
+ else {
+ fprintf(stderr, "Object ID: %08x\n", test_object2.id);
+ fprintf(stderr, "Object Type ID: %08x\n", test_object2.type->id);
+ }
+ uloop_init();
+ uloop_fd_add(&ctx->sock, ULOOP_READ | ULOOP_EDGE_TRIGGER);
+ uloop_run();
+
+ ubus_free(ctx);
+ return 0;
+}
--- /dev/null
+#ifndef __UBUS_COMMON_H
+#define __UBUS_COMMON_H
+
+#define UBUS_UNIX_SOCKET "./ubus.sock"
+
+#define UBUS_SIGNATURE_METHOD (BLOBMSG_TYPE_LAST + 1)
+#define UBUS_SIGNATURE_END (BLOBMSG_TYPE_LAST + 2)
+
+#ifndef ARRAY_SIZE
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
+#endif
+
+#define __init __attribute__((constructor))
+
+#endif
--- /dev/null
+#include <sys/socket.h>
+#include <sys/uio.h>
+#include <signal.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include <libubox/blob.h>
+#include <libubox/uloop.h>
+#include <libubox/usock.h>
+#include <libubox/list.h>
+
+#include "ubusd.h"
+
+static struct avl_tree clients;
+
+static struct ubus_msg_buf *ubus_msg_unshare(struct ubus_msg_buf *ub)
+{
+ ub = realloc(ub, sizeof(*ub) + ub->len);
+ if (!ub)
+ return NULL;
+
+ ub->refcount = 1;
+ memcpy(ub + 1, ub->data, ub->len);
+ ub->data = (void *) (ub + 1);
+ return ub;
+}
+
+struct ubus_msg_buf *ubus_msg_ref(struct ubus_msg_buf *ub)
+{
+ if (ub->refcount == ~0)
+ return ubus_msg_unshare(ub);
+
+ ub->refcount++;
+ return ub;
+}
+
+struct ubus_msg_buf *ubus_msg_new(void *data, int len, bool shared)
+{
+ struct ubus_msg_buf *ub;
+ int buflen = sizeof(*ub);
+
+ if (!shared)
+ buflen += len;
+
+ ub = calloc(1, buflen);
+ if (!ub)
+ return NULL;
+
+ if (shared) {
+ ub->refcount = ~0;
+ ub->data = data;
+ } else {
+ ub->refcount = 1;
+ ub->data = (void *) (ub + 1);
+ if (data)
+ memcpy(ub + 1, data, len);
+ }
+
+ ub->len = len;
+ return ub;
+}
+
+void ubus_msg_free(struct ubus_msg_buf *ub)
+{
+ switch (ub->refcount) {
+ case 1:
+ case ~0:
+ free(ub);
+ break;
+ default:
+ ub->refcount--;
+ break;
+ }
+}
+
+static int ubus_msg_writev(int fd, struct ubus_msg_buf *ub, int offset)
+{
+ struct iovec iov[2];
+
+ if (offset < sizeof(ub->hdr)) {
+ iov[0].iov_base = ((char *) &ub->hdr) + offset;
+ iov[0].iov_len = sizeof(ub->hdr) - offset;
+ iov[1].iov_base = (char *) ub->data;
+ iov[1].iov_len = ub->len;
+ return writev(fd, iov, 2);
+ } else {
+ offset -= sizeof(ub->hdr);
+ return write(fd, ((char *) ub->data) + offset, ub->len - offset);
+ }
+}
+
+/* takes the msgbuf reference */
+void ubus_msg_send(struct ubus_client *cl, struct ubus_msg_buf *ub)
+{
+ int written;
+
+ if (cl->buf_head)
+ goto queue;
+
+ written = ubus_msg_writev(cl->sock.fd, ub, 0);
+ if (written > 0 && written < ub->len + sizeof(ub->hdr)) {
+ cl->buf_head_ofs = written;
+
+ /* get an event once we can write to the socket again */
+ uloop_fd_add(&cl->sock, ULOOP_READ | ULOOP_WRITE | ULOOP_EDGE_TRIGGER);
+ goto queue;
+ }
+
+ ubus_msg_free(ub);
+ return;
+
+queue:
+ ub = ubus_msg_unshare(ub);
+ ub->next = NULL;
+ *cl->buf_tail = ub;
+ cl->buf_tail = &ub->next;
+}
+
+static void handle_client_disconnect(struct ubus_client *cl)
+{
+ struct ubus_object *obj;
+
+ while (!list_empty(&cl->objects)) {
+ obj = list_first_entry(&cl->objects, struct ubus_object, list);
+ ubusd_free_object(obj);
+ }
+
+ ubus_free_id(&clients, &cl->id);
+ uloop_fd_delete(&cl->sock);
+ close(cl->sock.fd);
+ free(cl);
+}
+
+static void client_cb(struct uloop_fd *sock, unsigned int events)
+{
+ struct ubus_client *cl = container_of(sock, struct ubus_client, sock);
+ struct ubus_msg_buf *ub;
+
+ /* first try to tx more pending data */
+ while (cl->buf_head) {
+ struct ubus_msg_buf *ub = cl->buf_head;
+ int written;
+
+ written = ubus_msg_writev(sock->fd, ub, cl->buf_head_ofs);
+ if (written < 0) {
+ switch(errno) {
+ case EINTR:
+ case EAGAIN:
+ break;
+ default:
+ goto disconnect;
+ }
+ break;
+ }
+ if (written == 0)
+ break;
+
+ cl->buf_head_ofs += written;
+ if (cl->buf_head_ofs < ub->len + sizeof(ub->hdr))
+ break;
+
+ cl->buf_head_ofs = 0;
+ cl->buf_head = ub->next;
+ if (!cl->buf_head)
+ cl->buf_tail = &cl->buf_head;
+ }
+
+ /* prevent further ULOOP_WRITE events if we don't have data
+ * to send anymore */
+ if (!cl->buf_head && (events & ULOOP_WRITE))
+ uloop_fd_add(sock, ULOOP_READ | ULOOP_EDGE_TRIGGER);
+
+retry:
+ if (!sock->eof && cl->pending_msg_offset < sizeof(cl->hdrbuf)) {
+ int offset = cl->pending_msg_offset;
+ int bytes;
+
+ bytes = read(sock->fd, (char *)&cl->hdrbuf + offset, sizeof(cl->hdrbuf) - offset);
+ if (bytes < 0)
+ goto out;
+
+ cl->pending_msg_offset += bytes;
+ if (cl->pending_msg_offset < sizeof(cl->hdrbuf))
+ goto out;
+
+ if (blob_len(&cl->hdrbuf.data) + sizeof(cl->hdrbuf) > UBUS_MAX_MSGLEN)
+ goto disconnect;
+
+ cl->pending_msg = ubus_msg_new(NULL, blob_raw_len(&cl->hdrbuf.data), false);
+ if (!cl->pending_msg)
+ goto disconnect;
+
+ memcpy(&cl->pending_msg->hdr, &cl->hdrbuf.hdr, sizeof(cl->hdrbuf.hdr));
+ memcpy(cl->pending_msg->data, &cl->hdrbuf.data, sizeof(cl->hdrbuf.data));
+ }
+
+ ub = cl->pending_msg;
+ if (ub) {
+ int offset = cl->pending_msg_offset - sizeof(ub->hdr);
+ int len = blob_raw_len(ub->data) - offset;
+ int bytes = 0;
+
+ if (len > 0) {
+ bytes = read(sock->fd, (char *) ub->data + offset, len);
+ if (bytes <= 0)
+ goto out;
+ }
+
+ if (bytes < len) {
+ cl->pending_msg_offset += bytes;
+ goto out;
+ }
+
+ /* accept message */
+ cl->pending_msg_offset = 0;
+ cl->pending_msg = NULL;
+ ubusd_receive_message(cl, ub);
+ goto retry;
+ }
+
+out:
+ if (!sock->eof || cl->buf_head)
+ return;
+
+disconnect:
+ handle_client_disconnect(cl);
+}
+
+struct ubus_client *ubusd_get_client_by_id(uint32_t id)
+{
+ struct ubus_id *clid;
+
+ clid = ubus_find_id(&clients, id);
+ if (!clid)
+ return NULL;
+
+ return container_of(clid, struct ubus_client, id);
+}
+
+static bool get_next_connection(int fd)
+{
+ struct ubus_client *cl;
+ int client_fd;
+
+ client_fd = accept(fd, NULL, 0);
+ if (client_fd < 0) {
+ switch (errno) {
+ case ECONNABORTED:
+ case EINTR:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ cl = calloc(1, sizeof(*cl));
+ cl->sock.fd = client_fd;
+
+ INIT_LIST_HEAD(&cl->objects);
+ if (!ubus_alloc_id(&clients, &cl->id))
+ goto error;
+
+ cl->sock.cb = client_cb;
+ uloop_fd_add(&cl->sock, ULOOP_READ | ULOOP_EDGE_TRIGGER);
+ if (!ubusd_send_hello(cl))
+ goto error_free;
+
+ return true;
+
+error_free:
+ ubus_free_id(&clients, &cl->id);
+error:
+ close(cl->sock.fd);
+ free(cl);
+ return true;
+}
+
+static void server_cb(struct uloop_fd *fd, unsigned int events)
+{
+ bool next;
+
+ do {
+ next = get_next_connection(fd->fd);
+ } while (next);
+}
+
+static struct uloop_fd server_fd = {
+ .cb = server_cb,
+};
+
+int main(int argc, char **argv)
+{
+ int ret = 0;
+
+ signal(SIGPIPE, SIG_IGN);
+
+ ubus_init_id_tree(&clients);
+
+ uloop_init();
+
+ unlink(UBUS_UNIX_SOCKET);
+ server_fd.fd = usock(USOCK_UNIX | USOCK_SERVER | USOCK_NONBLOCK, UBUS_UNIX_SOCKET, NULL);
+ if (server_fd.fd < 0) {
+ perror("usock");
+ ret = -1;
+ goto out;
+ }
+ uloop_fd_add(&server_fd, ULOOP_READ | ULOOP_EDGE_TRIGGER);
+
+ uloop_run();
+
+out:
+ uloop_done();
+ return ret;
+}
--- /dev/null
+#ifndef __UBUSD_H
+#define __UBUSD_H
+
+#include <libubox/list.h>
+#include <libubox/uloop.h>
+#include <libubox/blobmsg.h>
+#include "ubus_common.h"
+#include "ubusd_id.h"
+#include "ubusd_obj.h"
+#include "ubusmsg.h"
+
+#define UBUS_UNIX_SOCKET "./ubus.sock"
+#define UBUSD_CLIENT_BACKLOG 32
+#define UBUS_OBJ_HASH_BITS 4
+
+struct ubus_msg_buf {
+ struct ubus_msg_buf *next;
+ uint32_t refcount; /* ~0: uses external data buffer */
+ struct ubus_msghdr hdr;
+ struct blob_attr *data;
+ int len;
+};
+
+struct ubus_client {
+ struct ubus_id id;
+ struct uloop_fd sock;
+
+ struct {
+ struct ubus_msghdr hdr;
+ struct blob_attr data;
+ } hdrbuf;
+
+ struct list_head objects;
+
+ int pending_msg_offset;
+ struct ubus_msg_buf *pending_msg;
+
+ unsigned int buf_head_ofs;
+ struct ubus_msg_buf *buf_head;
+ struct ubus_msg_buf **buf_tail;
+
+ struct ubus_msg_buf *requests[UBUSD_CLIENT_BACKLOG];
+ unsigned int req_head, req_tail;
+};
+
+struct ubus_path {
+ struct list_head list;
+ const char name[];
+};
+
+struct ubus_msg_buf *ubus_msg_new(void *data, int len, bool shared);
+void ubus_msg_send(struct ubus_client *cl, struct ubus_msg_buf *ub);
+struct ubus_msg_buf *ubus_msg_ref(struct ubus_msg_buf *ub);
+void ubus_msg_free(struct ubus_msg_buf *ub);
+
+struct ubus_client *ubusd_get_client_by_id(uint32_t id);
+
+void ubusd_receive_message(struct ubus_client *cl, struct ubus_msg_buf *ub);
+bool ubusd_send_hello(struct ubus_client *cl);
+
+struct blob_attr **ubus_parse_msg(struct blob_attr *msg);
+
+
+#endif
--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include "ubusd_id.h"
+
+static int random_fd = -1;
+
+static int ubus_cmp_id(const void *k1, const void *k2, void *ptr)
+{
+ const uint32_t *id1 = k1, *id2 = k2;
+
+ if (*id1 < *id2)
+ return -1;
+ else
+ return *id1 > *id2;
+}
+
+void ubus_init_id_tree(struct avl_tree *tree)
+{
+ if (random_fd < 0) {
+ random_fd = open("/dev/urandom", O_RDONLY);
+ if (random_fd < 0) {
+ perror("open");
+ exit(1);
+ }
+ }
+
+ avl_init(tree, ubus_cmp_id, false, NULL);
+}
+
+bool ubus_alloc_id(struct avl_tree *tree, struct ubus_id *id)
+{
+ id->avl.key = &id->id;
+ do {
+ if (read(random_fd, &id->id, sizeof(id->id)) != sizeof(id->id))
+ return false;
+
+ if (!id->id)
+ continue;
+ } while (avl_insert(tree, &id->avl) != 0);
+
+ return true;
+}
+
--- /dev/null
+#ifndef __UBUSD_ID_H
+#define __UBUSD_ID_H
+
+#include <libubox/avl.h>
+#include <stdint.h>
+
+struct ubus_id {
+ struct avl_node avl;
+ uint32_t id;
+};
+
+void ubus_init_id_tree(struct avl_tree *tree);
+bool ubus_alloc_id(struct avl_tree *tree, struct ubus_id *id);
+
+static inline void ubus_free_id(struct avl_tree *tree, struct ubus_id *id)
+{
+ avl_delete(tree, &id->avl);
+}
+
+static inline struct ubus_id *ubus_find_id(struct avl_tree *tree, uint32_t id)
+{
+ struct avl_node *avl;
+
+ avl = avl_find(tree, &id);
+ if (!avl)
+ return NULL;
+
+ return container_of(avl, struct ubus_id, avl);
+}
+
+#endif
--- /dev/null
+#include "ubusd.h"
+#include "ubusd_obj.h"
+
+struct avl_tree obj_types;
+struct avl_tree objects;
+struct avl_tree path;
+
+static void ubus_unref_object_type(struct ubus_object_type *type)
+{
+ struct ubus_method *m;
+
+ if (--type->refcount > 0)
+ return;
+
+ while (!list_empty(&type->methods)) {
+ m = list_first_entry(&type->methods, struct ubus_method, list);
+ list_del(&m->list);
+ free(m);
+ }
+
+ ubus_free_id(&obj_types, &type->id);
+ free(type);
+}
+
+static bool ubus_create_obj_method(struct ubus_object_type *type, struct blob_attr *attr)
+{
+ struct ubus_method *m;
+ int bloblen = blob_raw_len(attr);
+
+ m = calloc(1, sizeof(*m) + bloblen);
+ if (!m)
+ return false;
+
+ list_add(&m->list, &type->methods);
+ memcpy(m->data, attr, bloblen);
+ m->name = blobmsg_name(m->data);
+
+ return true;
+}
+
+static struct ubus_object_type *ubus_create_obj_type(struct blob_attr *sig)
+{
+ struct ubus_object_type *type;
+ struct blob_attr *pos;
+ int rem;
+
+ type = calloc(1, sizeof(*type));
+ type->refcount = 1;
+
+ if (!ubus_alloc_id(&obj_types, &type->id))
+ goto error_free;
+
+ INIT_LIST_HEAD(&type->methods);
+
+ blob_for_each_attr(pos, sig, rem) {
+ if (!blobmsg_check_attr(pos, true))
+ goto error_unref;
+
+ if (!ubus_create_obj_method(type, pos))
+ goto error_unref;
+ }
+
+ return type;
+
+error_unref:
+ ubus_unref_object_type(type);
+ return NULL;
+
+error_free:
+ free(type);
+ return NULL;
+}
+
+static struct ubus_object_type *ubus_get_obj_type(uint32_t obj_id)
+{
+ struct ubus_object_type *type;
+ struct ubus_id *id;
+
+ id = ubus_find_id(&obj_types, obj_id);
+ if (!id)
+ return NULL;
+
+ type = container_of(id, struct ubus_object_type, id);
+ type->refcount++;
+ return type;
+}
+
+struct ubus_object *ubusd_create_object(struct ubus_client *cl, struct blob_attr **attr)
+{
+ struct ubus_object *obj;
+ struct ubus_object_type *type = NULL;
+
+ if (attr[UBUS_ATTR_OBJTYPE])
+ type = ubus_get_obj_type(blob_get_int32(attr[UBUS_ATTR_OBJTYPE]));
+ else if (attr[UBUS_ATTR_SIGNATURE])
+ type = ubus_create_obj_type(attr[UBUS_ATTR_SIGNATURE]);
+
+ if (!type)
+ return NULL;
+
+ obj = calloc(1, sizeof(*obj));
+ if (!ubus_alloc_id(&objects, &obj->id))
+ goto error_free;
+
+ if (attr[UBUS_ATTR_OBJPATH]) {
+ obj->path.key = strdup(blob_data(attr[UBUS_ATTR_OBJPATH]));
+ if (avl_insert(&path, &obj->path) != 0)
+ goto error_del_id;
+ }
+
+ obj->type = type;
+ list_add(&obj->list, &cl->objects);
+
+ return obj;
+
+error_del_id:
+ free(obj->path.key);
+ ubus_free_id(&objects, &obj->id);
+error_free:
+ ubus_unref_object_type(type);
+ free(obj);
+ return NULL;
+}
+
+void ubusd_free_object(struct ubus_object *obj)
+{
+ if (obj->path.key) {
+ avl_delete(&path, &obj->path);
+ free(obj->path.key);
+ }
+ list_del(&obj->list);
+ ubus_free_id(&objects, &obj->id);
+ ubus_unref_object_type(obj->type);
+ free(obj);
+}
+
+static int ubus_cmp_path(const void *k1, const void *k2, void *ptr)
+{
+ return strcmp(k1, k2);
+}
+
+static void __init ubusd_obj_init(void)
+{
+ ubus_init_id_tree(&objects);
+ ubus_init_id_tree(&obj_types);
+ avl_init(&path, ubus_cmp_path, false, NULL);
+}
--- /dev/null
+#ifndef __UBUSD_OBJ_H
+#define __UBUSD_OBJ_H
+
+#include "ubusd_id.h"
+
+extern struct avl_tree obj_types;
+extern struct avl_tree objects;
+extern struct avl_tree path;
+
+struct ubus_client;
+struct ubus_msg_buf;
+
+struct ubus_object_type {
+ struct ubus_id id;
+ int refcount;
+ struct list_head methods;
+};
+
+struct ubus_method {
+ struct list_head list;
+ const char *name;
+ struct blob_attr data[];
+};
+
+struct ubus_object {
+ struct ubus_id id;
+ struct list_head list;
+
+ struct ubus_object_type *type;
+
+ struct avl_node path;
+};
+
+struct ubus_object *ubusd_create_object(struct ubus_client *cl, struct blob_attr **attr);
+void ubusd_free_object(struct ubus_object *obj);
+
+#endif
--- /dev/null
+#include <arpa/inet.h>
+#include "ubusd.h"
+
+static struct blob_buf b;
+static struct ubus_msg_buf *retmsg;
+static int *retmsg_data;
+
+static struct blob_attr *attrbuf[UBUS_ATTR_MAX];
+
+typedef int (*ubus_cmd_cb)(struct ubus_client *cl, struct ubus_msg_buf *ub);
+
+static const struct blob_attr_info ubus_policy[UBUS_ATTR_MAX] = {
+ [UBUS_ATTR_SIGNATURE] = { .type = BLOB_ATTR_NESTED },
+ [UBUS_ATTR_OBJTYPE] = { .type = BLOB_ATTR_INT32 },
+ [UBUS_ATTR_OBJPATH] = { .type = BLOB_ATTR_STRING },
+};
+
+struct blob_attr **ubus_parse_msg(struct blob_attr *msg)
+{
+ blob_parse(msg, attrbuf, ubus_policy, UBUS_ATTR_MAX);
+ return attrbuf;
+}
+
+static void ubus_msg_init(struct ubus_msg_buf *ub, uint8_t type, uint16_t seq, uint32_t peer)
+{
+ ub->hdr.version = 0;
+ ub->hdr.type = type;
+ ub->hdr.seq = seq;
+ ub->hdr.peer = peer;
+}
+
+static struct ubus_msg_buf *ubus_msg_from_blob(bool shared)
+{
+ return ubus_msg_new(b.head, blob_raw_len(b.head), shared);
+}
+
+static struct ubus_msg_buf *ubus_reply_from_blob(struct ubus_msg_buf *ub, bool shared)
+{
+ struct ubus_msg_buf *new;
+
+ new = ubus_msg_new(b.head, blob_raw_len(b.head), shared);
+ if (!new)
+ return NULL;
+
+ ubus_msg_init(new, UBUS_MSG_DATA, ub->hdr.seq, ub->hdr.peer);
+ return new;
+}
+
+bool ubusd_send_hello(struct ubus_client *cl)
+{
+ struct ubus_msg_buf *ub;
+
+ blob_buf_init(&b, 0);
+ ub = ubus_msg_from_blob(true);
+ if (!ub)
+ return false;
+
+ ubus_msg_init(ub, UBUS_MSG_HELLO, 0, cl->id.id);
+ ubus_msg_send(cl, ub);
+ return true;
+}
+
+static int ubusd_send_pong(struct ubus_client *cl, struct ubus_msg_buf *ub)
+{
+ ub->hdr.type = UBUS_MSG_DATA;
+ ubus_msg_send(cl, ubus_msg_ref(ub));
+ return 0;
+}
+
+static int ubusd_handle_publish(struct ubus_client *cl, struct ubus_msg_buf *ub)
+{
+ struct ubus_object *obj;
+ struct blob_attr **attr;
+
+ attr = ubus_parse_msg(ub->data);
+ obj = ubusd_create_object(cl, attr);
+ if (!obj)
+ return UBUS_STATUS_INVALID_ARGUMENT;
+
+ blob_buf_init(&b, 0);
+ blob_put_int32(&b, UBUS_ATTR_OBJID, obj->id.id);
+ if (attr[UBUS_ATTR_SIGNATURE])
+ blob_put_int32(&b, UBUS_ATTR_OBJTYPE, obj->type->id.id);
+
+ ub = ubus_reply_from_blob(ub, true);
+ if (!ub)
+ return UBUS_STATUS_NO_DATA;
+
+ ubus_msg_send(cl, ub);
+ return 0;
+}
+
+static void ubusd_send_obj(struct ubus_client *cl, struct ubus_msg_buf *ub, struct ubus_object *obj)
+{
+ struct ubus_method *m;
+ void *s;
+
+ blob_buf_init(&b, 0);
+
+ if (obj->path.key)
+ blob_put_string(&b, UBUS_ATTR_OBJPATH, obj->path.key);
+ blob_put_int32(&b, UBUS_ATTR_OBJID, obj->id.id);
+
+ s = blob_nest_start(&b, UBUS_ATTR_SIGNATURE);
+ list_for_each_entry(m, &obj->type->methods, list)
+ blob_put(&b, blob_id(m->data), blob_data(m->data), blob_len(m->data));
+ blob_nest_end(&b, s);
+
+ ub = ubus_reply_from_blob(ub, true);
+ if (!ub)
+ return;
+
+ ubus_msg_send(cl, ub);
+}
+
+static int ubusd_handle_lookup(struct ubus_client *cl, struct ubus_msg_buf *ub)
+{
+ struct ubus_object *obj;
+ struct blob_attr **attr;
+ char *objpath;
+ bool wildcard = false;
+ bool found = false;
+ int len;
+
+ attr = ubus_parse_msg(ub->data);
+ if (!attr[UBUS_ATTR_OBJPATH]) {
+ avl_for_each_element(&path, obj, path)
+ ubusd_send_obj(cl, ub, obj);
+ return 0;
+ }
+
+ objpath = blob_data(attr[UBUS_ATTR_OBJPATH]);
+ len = strlen(objpath);
+ if (objpath[len - 1] != '*') {
+ obj = avl_find_element(&path, objpath, obj, path);
+ if (!obj)
+ return UBUS_STATUS_NOT_FOUND;
+
+ ubusd_send_obj(cl, ub, obj);
+ return 0;
+ }
+
+ objpath[--len] = 0;
+ wildcard = true;
+
+ obj = avl_find_ge_element(&path, objpath, obj, path);
+ if (!obj)
+ return UBUS_STATUS_NOT_FOUND;
+
+ while (!strncmp(objpath, obj->path.key, len)) {
+ found = true;
+ ubusd_send_obj(cl, ub, obj);
+ if (obj == avl_last_element(&path, obj, path))
+ break;
+ obj = avl_next_element(obj, path);
+ }
+
+ if (!found)
+ return UBUS_STATUS_NOT_FOUND;
+
+ return 0;
+}
+
+static const ubus_cmd_cb handlers[__UBUS_MSG_LAST] = {
+ [UBUS_MSG_PING] = ubusd_send_pong,
+ [UBUS_MSG_PUBLISH] = ubusd_handle_publish,
+ [UBUS_MSG_LOOKUP] = ubusd_handle_lookup,
+};
+
+void ubusd_receive_message(struct ubus_client *cl, struct ubus_msg_buf *ub)
+{
+ ubus_cmd_cb cb = NULL;
+ int ret;
+
+ retmsg->hdr.seq = ub->hdr.seq;
+ retmsg->hdr.peer = ub->hdr.peer;
+
+ if (ub->hdr.type < __UBUS_MSG_LAST)
+ cb = handlers[ub->hdr.type];
+
+ if (cb)
+ ret = cb(cl, ub);
+ else
+ ret = UBUS_STATUS_INVALID_COMMAND;
+
+ ubus_msg_free(ub);
+
+ *retmsg_data = htonl(ret);
+ ubus_msg_send(cl, ubus_msg_ref(retmsg));
+}
+
+static void __init ubusd_proto_init(void)
+{
+ blob_buf_init(&b, 0);
+ blob_put_int32(&b, UBUS_ATTR_STATUS, 0);
+
+ retmsg = ubus_msg_from_blob(false);
+ if (!retmsg)
+ exit(1);
+
+ retmsg->hdr.type = UBUS_MSG_STATUS;
+ retmsg_data = blob_data(blob_data(retmsg->data));
+}
--- /dev/null
+#ifndef __UBUSMSG_H
+#define __UBUSMSG_H
+
+#include <stdint.h>
+#include <libubox/blob.h>
+
+#define __packetdata __attribute__((packed)) __attribute__((__aligned__(4)))
+
+#define UBUS_MAX_MSGLEN 65535
+
+struct ubus_msghdr {
+ uint8_t version;
+ uint8_t type;
+ uint16_t seq;
+ uint32_t peer;
+ struct blob_attr data[];
+} __packetdata;
+
+enum ubus_msg_type {
+ /* initial server message */
+ UBUS_MSG_HELLO,
+
+ /* generic command response */
+ UBUS_MSG_STATUS,
+
+ /* data message response */
+ UBUS_MSG_DATA,
+
+ /* ping request */
+ UBUS_MSG_PING,
+
+ /* look up one or more objects */
+ UBUS_MSG_LOOKUP,
+
+ /* invoke a method on a single object */
+ UBUS_MSG_INVOKE,
+
+ /* publish an object */
+ UBUS_MSG_PUBLISH,
+
+ /* must be last */
+ __UBUS_MSG_LAST,
+};
+
+enum ubus_msg_attr {
+ UBUS_ATTR_UNSPEC,
+
+ UBUS_ATTR_STATUS,
+
+ UBUS_ATTR_OBJPATH,
+ UBUS_ATTR_OBJID,
+ UBUS_ATTR_METHOD,
+
+ UBUS_ATTR_OBJTYPE,
+ UBUS_ATTR_SIGNATURE,
+
+ UBUS_ATTR_DATA,
+
+ /* must be last */
+ UBUS_ATTR_MAX,
+};
+
+enum ubus_msg_status {
+ UBUS_STATUS_OK,
+ UBUS_STATUS_INVALID_COMMAND,
+ UBUS_STATUS_INVALID_ARGUMENT,
+ UBUS_STATUS_NOT_FOUND,
+ UBUS_STATUS_NO_DATA,
+ __UBUS_STATUS_LAST
+};
+
+#endif