Initial import
authorFelix Fietkau <nbd@openwrt.org>
Mon, 6 Dec 2010 02:51:58 +0000 (03:51 +0100)
committerFelix Fietkau <nbd@openwrt.org>
Sun, 30 Jan 2011 13:16:09 +0000 (14:16 +0100)
15 files changed:
.gitignore [new file with mode: 0644]
CMakeLists.txt [new file with mode: 0644]
cli.c [new file with mode: 0644]
libubus.c [new file with mode: 0644]
libubus.h [new file with mode: 0644]
listener.c [new file with mode: 0644]
ubus_common.h [new file with mode: 0644]
ubusd.c [new file with mode: 0644]
ubusd.h [new file with mode: 0644]
ubusd_id.c [new file with mode: 0644]
ubusd_id.h [new file with mode: 0644]
ubusd_obj.c [new file with mode: 0644]
ubusd_obj.h [new file with mode: 0644]
ubusd_proto.c [new file with mode: 0644]
ubusmsg.h [new file with mode: 0644]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..210271d
--- /dev/null
@@ -0,0 +1,9 @@
+Makefile
+CMakeCache.txt
+CMakeFiles
+*.cmake
+*.a
+ubus.sock
+listener
+ubusd
+ubus
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644 (file)
index 0000000..3114aa9
--- /dev/null
@@ -0,0 +1,16 @@
+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)
+
diff --git a/cli.c b/cli.c
new file mode 100644 (file)
index 0000000..ba6f30f
--- /dev/null
+++ b/cli.c
@@ -0,0 +1,73 @@
+#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;
+}
diff --git a/libubus.c b/libubus.c
new file mode 100644 (file)
index 0000000..5fdde30
--- /dev/null
+++ b/libubus.c
@@ -0,0 +1,515 @@
+#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);
+}
diff --git a/libubus.h b/libubus.h
new file mode 100644 (file)
index 0000000..bd3d736
--- /dev/null
+++ b/libubus.h
@@ -0,0 +1,141 @@
+#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);
+
+
diff --git a/listener.c b/listener.c
new file mode 100644 (file)
index 0000000..08b7c64
--- /dev/null
@@ -0,0 +1,63 @@
+#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;
+}
diff --git a/ubus_common.h b/ubus_common.h
new file mode 100644 (file)
index 0000000..e002b27
--- /dev/null
@@ -0,0 +1,15 @@
+#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
diff --git a/ubusd.c b/ubusd.c
new file mode 100644 (file)
index 0000000..87b9519
--- /dev/null
+++ b/ubusd.c
@@ -0,0 +1,316 @@
+#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;
+}
diff --git a/ubusd.h b/ubusd.h
new file mode 100644 (file)
index 0000000..c0b02f5
--- /dev/null
+++ b/ubusd.h
@@ -0,0 +1,64 @@
+#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
diff --git a/ubusd_id.c b/ubusd_id.c
new file mode 100644 (file)
index 0000000..9443db9
--- /dev/null
@@ -0,0 +1,46 @@
+#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;
+}
+
diff --git a/ubusd_id.h b/ubusd_id.h
new file mode 100644 (file)
index 0000000..ca69d9a
--- /dev/null
@@ -0,0 +1,31 @@
+#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
diff --git a/ubusd_obj.c b/ubusd_obj.c
new file mode 100644 (file)
index 0000000..5e08fab
--- /dev/null
@@ -0,0 +1,147 @@
+#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);
+}
diff --git a/ubusd_obj.h b/ubusd_obj.h
new file mode 100644 (file)
index 0000000..943ee13
--- /dev/null
@@ -0,0 +1,37 @@
+#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
diff --git a/ubusd_proto.c b/ubusd_proto.c
new file mode 100644 (file)
index 0000000..a202ace
--- /dev/null
@@ -0,0 +1,203 @@
+#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));
+}
diff --git a/ubusmsg.h b/ubusmsg.h
new file mode 100644 (file)
index 0000000..e62a393
--- /dev/null
+++ b/ubusmsg.h
@@ -0,0 +1,72 @@
+#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