Initial import
This commit is contained in:
commit
dbd4c2f121
15 changed files with 1748 additions and 0 deletions
9
.gitignore
vendored
Normal file
9
.gitignore
vendored
Normal file
|
@ -0,0 +1,9 @@
|
|||
Makefile
|
||||
CMakeCache.txt
|
||||
CMakeFiles
|
||||
*.cmake
|
||||
*.a
|
||||
ubus.sock
|
||||
listener
|
||||
ubusd
|
||||
ubus
|
16
CMakeLists.txt
Normal file
16
CMakeLists.txt
Normal file
|
@ -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)
|
||||
|
73
cli.c
Normal file
73
cli.c
Normal file
|
@ -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;
|
||||
}
|
515
libubus.c
Normal file
515
libubus.c
Normal file
|
@ -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);
|
||||
}
|
141
libubus.h
Normal file
141
libubus.h
Normal file
|
@ -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);
|
||||
|
||||
|
63
listener.c
Normal file
63
listener.c
Normal file
|
@ -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;
|
||||
}
|
15
ubus_common.h
Normal file
15
ubus_common.h
Normal file
|
@ -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
|
316
ubusd.c
Normal file
316
ubusd.c
Normal file
|
@ -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;
|
||||
}
|
64
ubusd.h
Normal file
64
ubusd.h
Normal file
|
@ -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
|
46
ubusd_id.c
Normal file
46
ubusd_id.c
Normal file
|
@ -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;
|
||||
}
|
||||
|
31
ubusd_id.h
Normal file
31
ubusd_id.h
Normal file
|
@ -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
|
147
ubusd_obj.c
Normal file
147
ubusd_obj.c
Normal file
|
@ -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);
|
||||
}
|
37
ubusd_obj.h
Normal file
37
ubusd_obj.h
Normal file
|
@ -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
|
203
ubusd_proto.c
Normal file
203
ubusd_proto.c
Normal file
|
@ -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));
|
||||
}
|
72
ubusmsg.h
Normal file
72
ubusmsg.h
Normal file
|
@ -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
|
Loading…
Add table
Reference in a new issue