From 2cdc93987e97c70cd7e7d1f4dab42e69e23a2dd2 Mon Sep 17 00:00:00 2001 From: jbnadal Date: Tue, 6 Dec 2016 17:44:32 +0100 Subject: [PATCH] Bump ubus-2016-07-02-053be7df871e05478284235732f8b0608089512f --- src/3P/ubus/.gitignore | 10 + src/3P/ubus/CMakeLists.txt | 55 + src/3P/ubus/cli.c | 587 +++++++++++ src/3P/ubus/examples/CMakeLists.txt | 12 + src/3P/ubus/examples/client.c | 242 +++++ src/3P/ubus/examples/count.c | 48 + src/3P/ubus/examples/count.h | 19 + src/3P/ubus/examples/server.c | 263 +++++ src/3P/ubus/libubus-acl.c | 153 +++ src/3P/ubus/libubus-internal.h | 32 + src/3P/ubus/libubus-io.c | 412 ++++++++ src/3P/ubus/libubus-obj.c | 261 +++++ src/3P/ubus/libubus-req.c | 478 +++++++++ src/3P/ubus/libubus-sub.c | 69 ++ src/3P/ubus/libubus.c | 373 +++++++ src/3P/ubus/libubus.h | 390 +++++++ .../libubus/builders/cmake/CMakeLists.txt | 43 + src/3P/ubus/lua/CMakeLists.txt | 52 + src/3P/ubus/lua/builders/linux-gcc/.gitignore | 1 + src/3P/ubus/lua/publisher.lua | 60 ++ src/3P/ubus/lua/subscriber.lua | 25 + src/3P/ubus/lua/test.lua | 54 + src/3P/ubus/lua/test_client.lua | 41 + src/3P/ubus/lua/ubus.c | 990 ++++++++++++++++++ src/3P/ubus/ubus_common.h | 24 + src/3P/ubus/ubusd.c | 422 ++++++++ src/3P/ubus/ubusd.h | 93 ++ .../ubus/ubusd/builders/cmake/CMakeLists.txt | 38 + src/3P/ubus/ubusd_acl.c | 495 +++++++++ src/3P/ubus/ubusd_acl.h | 28 + src/3P/ubus/ubusd_event.c | 271 +++++ src/3P/ubus/ubusd_id.c | 71 ++ src/3P/ubus/ubusd_id.h | 45 + src/3P/ubus/ubusd_monitor.c | 110 ++ src/3P/ubus/ubusd_obj.c | 236 +++++ src/3P/ubus/ubusd_obj.h | 84 ++ src/3P/ubus/ubusd_proto.c | 554 ++++++++++ src/3P/ubus/ubusmsg.h | 134 +++ .../ubus/ucli/builders/cmake/CMakeLists.txt | 30 + 39 files changed, 7305 insertions(+) create mode 100644 src/3P/ubus/.gitignore create mode 100644 src/3P/ubus/CMakeLists.txt create mode 100644 src/3P/ubus/cli.c create mode 100644 src/3P/ubus/examples/CMakeLists.txt create mode 100644 src/3P/ubus/examples/client.c create mode 100644 src/3P/ubus/examples/count.c create mode 100644 src/3P/ubus/examples/count.h create mode 100644 src/3P/ubus/examples/server.c create mode 100644 src/3P/ubus/libubus-acl.c create mode 100644 src/3P/ubus/libubus-internal.h create mode 100644 src/3P/ubus/libubus-io.c create mode 100644 src/3P/ubus/libubus-obj.c create mode 100644 src/3P/ubus/libubus-req.c create mode 100644 src/3P/ubus/libubus-sub.c create mode 100644 src/3P/ubus/libubus.c create mode 100644 src/3P/ubus/libubus.h create mode 100644 src/3P/ubus/libubus/builders/cmake/CMakeLists.txt create mode 100644 src/3P/ubus/lua/CMakeLists.txt create mode 100644 src/3P/ubus/lua/builders/linux-gcc/.gitignore create mode 100644 src/3P/ubus/lua/publisher.lua create mode 100644 src/3P/ubus/lua/subscriber.lua create mode 100755 src/3P/ubus/lua/test.lua create mode 100755 src/3P/ubus/lua/test_client.lua create mode 100644 src/3P/ubus/lua/ubus.c create mode 100644 src/3P/ubus/ubus_common.h create mode 100644 src/3P/ubus/ubusd.c create mode 100644 src/3P/ubus/ubusd.h create mode 100644 src/3P/ubus/ubusd/builders/cmake/CMakeLists.txt create mode 100644 src/3P/ubus/ubusd_acl.c create mode 100644 src/3P/ubus/ubusd_acl.h create mode 100644 src/3P/ubus/ubusd_event.c create mode 100644 src/3P/ubus/ubusd_id.c create mode 100644 src/3P/ubus/ubusd_id.h create mode 100644 src/3P/ubus/ubusd_monitor.c create mode 100644 src/3P/ubus/ubusd_obj.c create mode 100644 src/3P/ubus/ubusd_obj.h create mode 100644 src/3P/ubus/ubusd_proto.c create mode 100644 src/3P/ubus/ubusmsg.h create mode 100644 src/3P/ubus/ucli/builders/cmake/CMakeLists.txt diff --git a/src/3P/ubus/.gitignore b/src/3P/ubus/.gitignore new file mode 100644 index 00000000..8a09daf7 --- /dev/null +++ b/src/3P/ubus/.gitignore @@ -0,0 +1,10 @@ +Makefile +CMakeCache.txt +CMakeFiles +*.cmake +*.a +*.so +*.dylib +examples/server +examples/client +install_manifest.txt diff --git a/src/3P/ubus/CMakeLists.txt b/src/3P/ubus/CMakeLists.txt new file mode 100644 index 00000000..7123096a --- /dev/null +++ b/src/3P/ubus/CMakeLists.txt @@ -0,0 +1,55 @@ +cmake_minimum_required(VERSION 2.6) + +PROJECT(ubus C) +ADD_DEFINITIONS(-Os -Wall -Werror --std=gnu99 -g3 -Wmissing-declarations) + +OPTION(BUILD_LUA "build Lua plugin" ON) +OPTION(BUILD_EXAMPLES "build examples" ON) + +SET(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "") +SET(UBUS_UNIX_SOCKET "/var/run/ubus.sock") +SET(UBUS_MAX_MSGLEN 1048576) + +ADD_DEFINITIONS( -DUBUS_UNIX_SOCKET="${UBUS_UNIX_SOCKET}") +ADD_DEFINITIONS( -DUBUS_MAX_MSGLEN=${UBUS_MAX_MSGLEN}) + +IF(APPLE) + INCLUDE_DIRECTORIES(/opt/local/include) + LINK_DIRECTORIES(/opt/local/lib) +ENDIF() + +IF(BUILD_STATIC) + FIND_LIBRARY(ubox_library NAMES ubox.a) + FIND_LIBRARY(blob_library NAMES blobmsg_json.a) +ELSE(BUILD_STATIC) + FIND_LIBRARY(ubox_library NAMES ubox) + FIND_LIBRARY(blob_library NAMES blobmsg_json) +ENDIF(BUILD_STATIC) + +FIND_PATH(ubox_include_dir libubox/usock.h) +INCLUDE_DIRECTORIES(${ubox_include_dir}) + +ADD_LIBRARY(ubus SHARED libubus.c libubus-io.c libubus-obj.c libubus-sub.c libubus-req.c libubus-acl.c) +TARGET_LINK_LIBRARIES(ubus ${ubox_library}) + +find_library(json NAMES json-c json) + +ADD_EXECUTABLE(ubusd ubusd.c ubusd_id.c ubusd_obj.c ubusd_proto.c ubusd_event.c ubusd_acl.c ubusd_monitor.c) +TARGET_LINK_LIBRARIES(ubusd ${ubox_library} ${blob_library} ${json}) + +ADD_EXECUTABLE(cli cli.c) +SET_TARGET_PROPERTIES(cli PROPERTIES OUTPUT_NAME ubus) +TARGET_LINK_LIBRARIES(cli ubus ${ubox_library} ${blob_library} ${json}) + +ADD_SUBDIRECTORY(lua) +ADD_SUBDIRECTORY(examples) + +INSTALL(TARGETS ubus cli + LIBRARY DESTINATION lib + RUNTIME DESTINATION bin +) +INSTALL(TARGETS ubusd + RUNTIME DESTINATION sbin +) + +INSTALL(FILES ubusmsg.h ubus_common.h libubus.h DESTINATION include) diff --git a/src/3P/ubus/cli.c b/src/3P/ubus/cli.c new file mode 100644 index 00000000..c5cbfc3b --- /dev/null +++ b/src/3P/ubus/cli.c @@ -0,0 +1,587 @@ +/* + * Copyright (C) 2011 Felix Fietkau + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include + +#include +#include "libubus.h" + +static struct blob_buf b; +static int timeout = 30; +static bool simple_output = false; +static int verbose = 0; +static int monitor_dir = -1; +static uint32_t monitor_mask; +static const char * const monitor_types[] = { + [UBUS_MSG_HELLO] = "hello", + [UBUS_MSG_STATUS] = "status", + [UBUS_MSG_DATA] = "data", + [UBUS_MSG_PING] = "ping", + [UBUS_MSG_LOOKUP] = "lookup", + [UBUS_MSG_INVOKE] = "invoke", + [UBUS_MSG_ADD_OBJECT] = "add_object", + [UBUS_MSG_REMOVE_OBJECT] = "remove_object", + [UBUS_MSG_SUBSCRIBE] = "subscribe", + [UBUS_MSG_UNSUBSCRIBE] = "unsubscribe", + [UBUS_MSG_NOTIFY] = "notify", +}; + +static const char *format_type(void *priv, struct blob_attr *attr) +{ + static const char * const attr_types[] = { + [BLOBMSG_TYPE_INT8] = "\"Boolean\"", + [BLOBMSG_TYPE_INT32] = "\"Integer\"", + [BLOBMSG_TYPE_STRING] = "\"String\"", + [BLOBMSG_TYPE_ARRAY] = "\"Array\"", + [BLOBMSG_TYPE_TABLE] = "\"Table\"", + }; + const char *type = NULL; + int typeid; + + if (blob_id(attr) != BLOBMSG_TYPE_INT32) + return NULL; + + typeid = blobmsg_get_u32(attr); + if (typeid < ARRAY_SIZE(attr_types)) + type = attr_types[typeid]; + if (!type) + type = "\"(unknown)\""; + + return type; +} + +static void receive_list_result(struct ubus_context *ctx, struct ubus_object_data *obj, void *priv) +{ + struct blob_attr *cur; + char *s; + int rem; + + if (simple_output || !verbose) { + printf("%s\n", obj->path); + return; + } + + printf("'%s' @%08x\n", obj->path, obj->id); + + if (!obj->signature) + return; + + blob_for_each_attr(cur, obj->signature, rem) { + s = blobmsg_format_json_with_cb(cur, false, format_type, NULL, -1); + printf("\t%s\n", s); + free(s); + } +} + +static void receive_call_result_data(struct ubus_request *req, int type, struct blob_attr *msg) +{ + char *str; + if (!msg) + return; + + str = blobmsg_format_json_indent(msg, true, simple_output ? -1 : 0); + printf("%s\n", str); + free(str); +} + +static void receive_event(struct ubus_context *ctx, struct ubus_event_handler *ev, + const char *type, struct blob_attr *msg) +{ + char *str; + + str = blobmsg_format_json(msg, true); + printf("{ \"%s\": %s }\n", type, str); + fflush(stdout); + free(str); +} + +static int ubus_cli_list(struct ubus_context *ctx, int argc, char **argv) +{ + const char *path = NULL; + + if (argc > 1) + return -2; + + if (argc == 1) + path = argv[0]; + + return ubus_lookup(ctx, path, receive_list_result, NULL); +} + +static int ubus_cli_call(struct ubus_context *ctx, int argc, char **argv) +{ + uint32_t id; + int ret; + + if (argc < 2 || argc > 3) + return -2; + + blob_buf_init(&b, 0); + if (argc == 3 && !blobmsg_add_json_from_string(&b, argv[2])) { + if (!simple_output) + fprintf(stderr, "Failed to parse message data\n"); + return -1; + } + + ret = ubus_lookup_id(ctx, argv[0], &id); + if (ret) + return ret; + + return ubus_invoke(ctx, id, argv[1], b.head, receive_call_result_data, NULL, timeout * 1000); +} + +struct cli_listen_data { + struct uloop_timeout timeout; + struct ubus_event_handler ev; + bool timed_out; +}; + +static void listen_timeout(struct uloop_timeout *timeout) +{ + struct cli_listen_data *data = container_of(timeout, struct cli_listen_data, timeout); + data->timed_out = true; + uloop_end(); +} + +static int ubus_cli_listen(struct ubus_context *ctx, int argc, char **argv) +{ + struct cli_listen_data data = { + .timeout.cb = listen_timeout, + .ev.cb = receive_event, + .timed_out = false, + }; + const char *event; + int ret = 0; + + if (argc > 0) { + event = argv[0]; + } else { + event = "*"; + argc = 1; + } + + do { + ret = ubus_register_event_handler(ctx, &data.ev, event); + if (ret) + break; + + argv++; + argc--; + if (argc <= 0) + break; + + event = argv[0]; + } while (1); + + if (ret) { + if (!simple_output) + fprintf(stderr, "Error while registering for event '%s': %s\n", + event, ubus_strerror(ret)); + return -1; + } + + uloop_init(); + ubus_add_uloop(ctx); + uloop_timeout_set(&data.timeout, timeout * 1000); + uloop_run(); + uloop_done(); + + return 0; +} + +static int ubus_cli_send(struct ubus_context *ctx, int argc, char **argv) +{ + if (argc < 1 || argc > 2) + return -2; + + blob_buf_init(&b, 0); + + if (argc == 2 && !blobmsg_add_json_from_string(&b, argv[1])) { + if (!simple_output) + fprintf(stderr, "Failed to parse message data\n"); + return -1; + } + + return ubus_send_event(ctx, argv[0], b.head); +} + +struct cli_wait_data { + struct uloop_timeout timeout; + struct ubus_event_handler ev; + char **pending; + int n_pending; +}; + +static void wait_check_object(struct cli_wait_data *data, const char *path) +{ + int i; + + for (i = 0; i < data->n_pending; i++) { + if (strcmp(path, data->pending[i]) != 0) + continue; + + data->n_pending--; + if (i == data->n_pending) + break; + + memmove(&data->pending[i], &data->pending[i + 1], + (data->n_pending - i) * sizeof(*data->pending)); + i--; + } + + if (!data->n_pending) + uloop_end(); +} + +static void wait_event_cb(struct ubus_context *ctx, struct ubus_event_handler *ev, + const char *type, struct blob_attr *msg) +{ + static const struct blobmsg_policy policy = { + "path", BLOBMSG_TYPE_STRING + }; + struct cli_wait_data *data = container_of(ev, struct cli_wait_data, ev); + struct blob_attr *attr; + const char *path; + + if (strcmp(type, "ubus.object.add") != 0) + return; + + blobmsg_parse(&policy, 1, &attr, blob_data(msg), blob_len(msg)); + if (!attr) + return; + + path = blobmsg_data(attr); + wait_check_object(data, path); +} + +static void wait_list_cb(struct ubus_context *ctx, struct ubus_object_data *obj, void *priv) +{ + struct cli_wait_data *data = priv; + + wait_check_object(data, obj->path); +} + + +static void wait_timeout(struct uloop_timeout *timeout) +{ + uloop_end(); +} + +static int ubus_cli_wait_for(struct ubus_context *ctx, int argc, char **argv) +{ + struct cli_wait_data data = { + .timeout.cb = wait_timeout, + .ev.cb = wait_event_cb, + .pending = argv, + .n_pending = argc, + }; + int ret; + + if (argc < 1) + return -2; + + uloop_init(); + ubus_add_uloop(ctx); + + ret = ubus_lookup(ctx, NULL, wait_list_cb, &data); + if (ret) + return ret; + + if (!data.n_pending) + return ret; + + ret = ubus_register_event_handler(ctx, &data.ev, "ubus.object.add"); + if (ret) + return ret; + + uloop_timeout_set(&data.timeout, timeout * 1000); + uloop_run(); + uloop_done(); + + if (data.n_pending) + return UBUS_STATUS_TIMEOUT; + + return ret; +} + +static const char * +ubus_cli_msg_type(uint32_t type) +{ + const char *ret = NULL; + static char unk_type[16]; + + + if (type < ARRAY_SIZE(monitor_types)) + ret = monitor_types[type]; + + if (!ret) { + snprintf(unk_type, sizeof(unk_type), "%d", type); + ret = unk_type; + } + + return ret; +} + +static char * +ubus_cli_get_monitor_data(struct blob_attr *data) +{ + static const struct blob_attr_info policy[UBUS_ATTR_MAX] = { + [UBUS_ATTR_STATUS] = { .type = BLOB_ATTR_INT32 }, + [UBUS_ATTR_OBJPATH] = { .type = BLOB_ATTR_STRING }, + [UBUS_ATTR_OBJID] = { .type = BLOB_ATTR_INT32 }, + [UBUS_ATTR_METHOD] = { .type = BLOB_ATTR_STRING }, + [UBUS_ATTR_OBJTYPE] = { .type = BLOB_ATTR_INT32 }, + [UBUS_ATTR_SIGNATURE] = { .type = BLOB_ATTR_NESTED }, + [UBUS_ATTR_DATA] = { .type = BLOB_ATTR_NESTED }, + [UBUS_ATTR_ACTIVE] = { .type = BLOB_ATTR_INT8 }, + [UBUS_ATTR_NO_REPLY] = { .type = BLOB_ATTR_INT8 }, + [UBUS_ATTR_USER] = { .type = BLOB_ATTR_STRING }, + [UBUS_ATTR_GROUP] = { .type = BLOB_ATTR_STRING }, + }; + static const char * const names[UBUS_ATTR_MAX] = { + [UBUS_ATTR_STATUS] = "status", + [UBUS_ATTR_OBJPATH] = "objpath", + [UBUS_ATTR_OBJID] = "objid", + [UBUS_ATTR_METHOD] = "method", + [UBUS_ATTR_OBJTYPE] = "objtype", + [UBUS_ATTR_SIGNATURE] = "signature", + [UBUS_ATTR_DATA] = "data", + [UBUS_ATTR_ACTIVE] = "active", + [UBUS_ATTR_NO_REPLY] = "no_reply", + [UBUS_ATTR_USER] = "user", + [UBUS_ATTR_GROUP] = "group", + }; + struct blob_attr *tb[UBUS_ATTR_MAX]; + int i; + + blob_buf_init(&b, 0); + blob_parse(data, tb, policy, UBUS_ATTR_MAX); + + for (i = 0; i < UBUS_ATTR_MAX; i++) { + const char *n = names[i]; + struct blob_attr *v = tb[i]; + + if (!tb[i] || !n) + continue; + + switch(policy[i].type) { + case BLOB_ATTR_INT32: + blobmsg_add_u32(&b, n, blob_get_int32(v)); + break; + case BLOB_ATTR_STRING: + blobmsg_add_string(&b, n, blob_data(v)); + break; + case BLOB_ATTR_INT8: + blobmsg_add_u8(&b, n, !!blob_get_int8(v)); + break; + case BLOB_ATTR_NESTED: + blobmsg_add_field(&b, BLOBMSG_TYPE_TABLE, n, blobmsg_data(v), blobmsg_data_len(v)); + break; + } + } + + return blobmsg_format_json(b.head, true); +} + +static void +ubus_cli_monitor_cb(struct ubus_context *ctx, uint32_t seq, struct blob_attr *msg) +{ + static const struct blob_attr_info policy[UBUS_MONITOR_MAX] = { + [UBUS_MONITOR_CLIENT] = { .type = BLOB_ATTR_INT32 }, + [UBUS_MONITOR_PEER] = { .type = BLOB_ATTR_INT32 }, + [UBUS_MONITOR_SEND] = { .type = BLOB_ATTR_INT8 }, + [UBUS_MONITOR_TYPE] = { .type = BLOB_ATTR_INT32 }, + [UBUS_MONITOR_DATA] = { .type = BLOB_ATTR_NESTED }, + }; + struct blob_attr *tb[UBUS_MONITOR_MAX]; + uint32_t client, peer, type; + bool send; + char *data; + + blob_parse(msg, tb, policy, UBUS_MONITOR_MAX); + + if (!tb[UBUS_MONITOR_CLIENT] || + !tb[UBUS_MONITOR_PEER] || + !tb[UBUS_MONITOR_SEND] || + !tb[UBUS_MONITOR_TYPE] || + !tb[UBUS_MONITOR_DATA]) { + printf("Invalid monitor msg\n"); + return; + } + + send = blob_get_int32(tb[UBUS_MONITOR_SEND]); + client = blob_get_int32(tb[UBUS_MONITOR_CLIENT]); + peer = blob_get_int32(tb[UBUS_MONITOR_PEER]); + type = blob_get_int32(tb[UBUS_MONITOR_TYPE]); + + if (monitor_mask && type < 32 && !(monitor_mask & (1 << type))) + return; + + if (monitor_dir >= 0 && send != monitor_dir) + return; + + data = ubus_cli_get_monitor_data(tb[UBUS_MONITOR_DATA]); + printf("%s %08x #%08x %14s: %s\n", send ? "->" : "<-", client, peer, ubus_cli_msg_type(type), data); + free(data); + fflush(stdout); +} + +static int ubus_cli_monitor(struct ubus_context *ctx, int argc, char **argv) +{ + int ret; + + uloop_init(); + ubus_add_uloop(ctx); + ctx->monitor_cb = ubus_cli_monitor_cb; + ret = ubus_monitor_start(ctx); + if (ret) + return ret; + + uloop_run(); + uloop_done(); + + ubus_monitor_stop(ctx); + return 0; +} + +static int add_monitor_type(const char *type) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(monitor_types); i++) { + if (!monitor_types[i] || strcmp(monitor_types[i], type) != 0) + continue; + + monitor_mask |= 1 << i; + return 0; + } + + return -1; +} + +static int usage(const char *prog) +{ + fprintf(stderr, + "Usage: %s [] [arguments...]\n" + "Options:\n" + " -s : Set the unix domain socket to connect to\n" + " -t : Set the timeout (in seconds) for a command to complete\n" + " -S: Use simplified output (for scripts)\n" + " -v: More verbose output\n" + " -m : (for monitor): include a specific message type\n" + " (can be used more than once)\n" + " -M (for monitor): only capture received or transmitted traffic\n" + "\n" + "Commands:\n" + " - list [] List objects\n" + " - call [] Call an object method\n" + " - listen [...] Listen for events\n" + " - send [] Send an event\n" + " - wait_for [...] Wait for multiple objects to appear on ubus\n" + " - monitor Monitor ubus traffic\n" + "\n", prog); + return 1; +} + + +static struct { + const char *name; + int (*cb)(struct ubus_context *ctx, int argc, char **argv); +} commands[] = { + { "list", ubus_cli_list }, + { "call", ubus_cli_call }, + { "listen", ubus_cli_listen }, + { "send", ubus_cli_send }, + { "wait_for", ubus_cli_wait_for }, + { "monitor", ubus_cli_monitor }, +}; + +int main(int argc, char **argv) +{ + const char *progname, *ubus_socket = NULL; + struct ubus_context *ctx; + char *cmd; + int ret = 0; + int i, ch; + + progname = argv[0]; + + while ((ch = getopt(argc, argv, "m:M:vs:t:S")) != -1) { + switch (ch) { + case 's': + ubus_socket = optarg; + break; + case 't': + timeout = atoi(optarg); + break; + case 'S': + simple_output = true; + break; + case 'v': + verbose++; + break; + case 'm': + if (add_monitor_type(optarg)) + return usage(progname); + break; + case 'M': + switch (optarg[0]) { + case 'r': + monitor_dir = 0; + break; + case 't': + monitor_dir = 1; + break; + default: + return usage(progname); + } + break; + default: + return usage(progname); + } + } + + argc -= optind; + argv += optind; + + cmd = argv[0]; + if (argc < 1) + return usage(progname); + + ctx = ubus_connect(ubus_socket); + if (!ctx) { + if (!simple_output) + fprintf(stderr, "Failed to connect to ubus\n"); + return -1; + } + + argv++; + argc--; + + ret = -2; + for (i = 0; i < ARRAY_SIZE(commands); i++) { + if (strcmp(commands[i].name, cmd) != 0) + continue; + + ret = commands[i].cb(ctx, argc, argv); + break; + } + + if (ret > 0 && !simple_output) + fprintf(stderr, "Command failed: %s\n", ubus_strerror(ret)); + else if (ret == -2) + usage(progname); + + ubus_free(ctx); + return ret; +} diff --git a/src/3P/ubus/examples/CMakeLists.txt b/src/3P/ubus/examples/CMakeLists.txt new file mode 100644 index 00000000..81f99975 --- /dev/null +++ b/src/3P/ubus/examples/CMakeLists.txt @@ -0,0 +1,12 @@ +cmake_minimum_required(VERSION 2.6) + +ADD_DEFINITIONS(-I..) +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/..) + +IF (BUILD_EXAMPLES) + ADD_EXECUTABLE(server server.c count.c) + TARGET_LINK_LIBRARIES(server ubus ${ubox_library} ${blob_library} ${json}) + + ADD_EXECUTABLE(client client.c count.c) + TARGET_LINK_LIBRARIES(client ubus ${ubox_library}) +ENDIF() diff --git a/src/3P/ubus/examples/client.c b/src/3P/ubus/examples/client.c new file mode 100644 index 00000000..7ef56634 --- /dev/null +++ b/src/3P/ubus/examples/client.c @@ -0,0 +1,242 @@ +/* + * Copyright (C) 2011 Felix Fietkau + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include + +#include + +#include "libubus.h" +#include "count.h" + +static struct ubus_context *ctx; +static struct blob_buf b; + +static void test_client_subscribe_cb(struct ubus_context *ctx, struct ubus_object *obj) +{ + fprintf(stderr, "Subscribers active: %d\n", obj->has_subscribers); +} + +static struct ubus_object test_client_object = { + .subscribe_cb = test_client_subscribe_cb, +}; + +static void test_client_notify_cb(struct uloop_timeout *timeout) +{ + static int counter = 0; + int err; + struct timeval tv1, tv2; + int max = 1000; + long delta; + int i = 0; + + blob_buf_init(&b, 0); + blobmsg_add_u32(&b, "counter", counter++); + + gettimeofday(&tv1, NULL); + for (i = 0; i < max; i++) + err = ubus_notify(ctx, &test_client_object, "ping", b.head, 1000); + gettimeofday(&tv2, NULL); + if (err) + fprintf(stderr, "Notify failed: %s\n", ubus_strerror(err)); + + delta = (tv2.tv_sec - tv1.tv_sec) * 1000000 + (tv2.tv_usec - tv1.tv_usec); + fprintf(stderr, "Avg time per iteration: %ld usec\n", delta / max); + + uloop_timeout_set(timeout, 1000); +} + +enum { + RETURN_CODE, + __RETURN_MAX, +}; + +static const struct blobmsg_policy return_policy[__RETURN_MAX] = { + [RETURN_CODE] = { .name = "rc", .type = BLOBMSG_TYPE_INT32 }, +}; + +static void test_count_data_cb(struct ubus_request *req, + int type, struct blob_attr *msg) +{ + struct blob_attr *tb[__RETURN_MAX]; + int rc; + uint32_t count_to = *(uint32_t *)req->priv; + + blobmsg_parse(return_policy, __RETURN_MAX, tb, blob_data(msg), blob_len(msg)); + + if (!tb[RETURN_CODE]) { + fprintf(stderr, "No return code received from server\n"); + return; + } + rc = blobmsg_get_u32(tb[RETURN_CODE]); + if (rc) + fprintf(stderr, "Corruption of data with count up to '%u'\n", count_to); + else + fprintf(stderr, "Server validated our count up to '%u'\n", count_to); +} + +static void test_count(struct uloop_timeout *timeout) +{ + enum { + COUNT_TO_MIN = 10000, + COUNT_TO_MAX = 1000000, + PROGRESSION = 100, + }; + + uint32_t id; + static uint32_t count_to = 100000; + static int count_progression = PROGRESSION; + char *s; + + if (count_to <= COUNT_TO_MIN) + count_progression = PROGRESSION; + else if (count_to >= COUNT_TO_MAX) + count_progression = -PROGRESSION; + + count_to += count_progression; + + s = count_to_number(count_to); + if (!s) { + fprintf(stderr, "Could not allocate memory to count up to '%u'\n", count_to); + return; + } + + fprintf(stderr, "Sending count up to '%u'; string has length '%u'\n", + count_to, (uint32_t)strlen(s)); + blob_buf_init(&b, 0); + blobmsg_add_u32(&b, "to", count_to); + blobmsg_add_string(&b, "string", s); + + if (ubus_lookup_id(ctx, "test", &id)) { + free(s); + fprintf(stderr, "Failed to look up test object\n"); + return; + } + + ubus_invoke(ctx, id, "count", b.head, test_count_data_cb, &count_to, 5000); + + free(s); + + uloop_timeout_set(timeout, 2000); +} + +static struct uloop_timeout notify_timer = { + .cb = test_client_notify_cb, +}; + +static struct uloop_timeout count_timer = { + .cb = test_count, +}; + +static void test_client_fd_data_cb(struct ustream *s, int bytes) +{ + char *data, *sep; + int len; + + data = ustream_get_read_buf(s, &len); + if (len < 1) + return; + + sep = strchr(data, '\n'); + if (!sep) + return; + + *sep = 0; + fprintf(stderr, "Got line: %s\n", data); + ustream_consume(s, sep + 1 - data); +} + +static void test_client_fd_cb(struct ubus_request *req, int fd) +{ + static struct ustream_fd test_fd; + + fprintf(stderr, "Got fd from the server, watching...\n"); + + test_fd.stream.notify_read = test_client_fd_data_cb; + ustream_fd_init(&test_fd, fd); +} + +static void test_client_complete_cb(struct ubus_request *req, int ret) +{ + fprintf(stderr, "completed request, ret: %d\n", ret); +} + +static void client_main(void) +{ + static struct ubus_request req; + uint32_t id; + int ret; + + ret = ubus_add_object(ctx, &test_client_object); + if (ret) { + fprintf(stderr, "Failed to add_object object: %s\n", ubus_strerror(ret)); + return; + } + + if (ubus_lookup_id(ctx, "test", &id)) { + fprintf(stderr, "Failed to look up test object\n"); + return; + } + + blob_buf_init(&b, 0); + blobmsg_add_u32(&b, "id", test_client_object.id); + ubus_invoke(ctx, id, "watch", b.head, NULL, 0, 3000); + test_client_notify_cb(¬ify_timer); + + blob_buf_init(&b, 0); + blobmsg_add_string(&b, "msg", "blah"); + ubus_invoke_async(ctx, id, "hello", b.head, &req); + req.fd_cb = test_client_fd_cb; + req.complete_cb = test_client_complete_cb; + ubus_complete_request_async(ctx, &req); + + uloop_timeout_set(&count_timer, 2000); + + uloop_run(); +} + +int main(int argc, char **argv) +{ + const char *ubus_socket = NULL; + int ch; + + while ((ch = getopt(argc, argv, "cs:")) != -1) { + switch (ch) { + case 's': + ubus_socket = optarg; + break; + default: + break; + } + } + + argc -= optind; + argv += optind; + + uloop_init(); + + ctx = ubus_connect(ubus_socket); + if (!ctx) { + fprintf(stderr, "Failed to connect to ubus\n"); + return -1; + } + + ubus_add_uloop(ctx); + + client_main(); + + ubus_free(ctx); + uloop_done(); + + return 0; +} diff --git a/src/3P/ubus/examples/count.c b/src/3P/ubus/examples/count.c new file mode 100644 index 00000000..e3e9c8ac --- /dev/null +++ b/src/3P/ubus/examples/count.c @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2011 Felix Fietkau + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include "count.h" + +char *count_to_number(uint32_t num) +{ + uint32_t ptr = 0, size = 0; + uint32_t written = 0, i; + int new_line_every_n_numbers = 30; + char *s; + + for (i=0; i < num; ++i) { + size += snprintf(NULL, 0, "%u ", i); + if (i > 0 && i % new_line_every_n_numbers == 0) + size++; + } + size++; /* one for null char */ + + s = calloc(size, sizeof(char)); + if (!s) + goto out; + + for (i=0; i < num; ++i) { + written = sprintf(&s[ptr], "%u ", i); + ptr += written; + if (i > 0 && i % new_line_every_n_numbers == 0) { + sprintf(&s[ptr], "\n"); + ptr++; + } + } + +out: + return s; +} diff --git a/src/3P/ubus/examples/count.h b/src/3P/ubus/examples/count.h new file mode 100644 index 00000000..1f90f21d --- /dev/null +++ b/src/3P/ubus/examples/count.h @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2011 Felix Fietkau + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __COUNT_H +#define __COUNT_H + +char *count_to_number(uint32_t num); + +#endif diff --git a/src/3P/ubus/examples/server.c b/src/3P/ubus/examples/server.c new file mode 100644 index 00000000..e0cde0bf --- /dev/null +++ b/src/3P/ubus/examples/server.c @@ -0,0 +1,263 @@ +/* + * Copyright (C) 2011-2014 Felix Fietkau + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include + +#include +#include "libubus.h" +#include "count.h" + +static struct ubus_context *ctx; +static struct ubus_subscriber test_event; +static struct blob_buf b; + +enum { + HELLO_ID, + HELLO_MSG, + __HELLO_MAX +}; + +static const struct blobmsg_policy hello_policy[] = { + [HELLO_ID] = { .name = "id", .type = BLOBMSG_TYPE_INT32 }, + [HELLO_MSG] = { .name = "msg", .type = BLOBMSG_TYPE_STRING }, +}; + +struct hello_request { + struct ubus_request_data req; + struct uloop_timeout timeout; + int fd; + int idx; + char data[]; +}; + +static void test_hello_fd_reply(struct uloop_timeout *t) +{ + struct hello_request *req = container_of(t, struct hello_request, timeout); + char *data; + + data = alloca(strlen(req->data) + 32); + sprintf(data, "msg%d: %s\n", ++req->idx, req->data); + if (write(req->fd, data, strlen(data)) < 0) { + close(req->fd); + free(req); + return; + } + + uloop_timeout_set(&req->timeout, 1000); +} + +static void test_hello_reply(struct uloop_timeout *t) +{ + struct hello_request *req = container_of(t, struct hello_request, timeout); + int fds[2]; + + blob_buf_init(&b, 0); + blobmsg_add_string(&b, "message", req->data); + ubus_send_reply(ctx, &req->req, b.head); + + if (pipe(fds) == -1) { + fprintf(stderr, "Failed to create pipe\n"); + return; + } + ubus_request_set_fd(ctx, &req->req, fds[0]); + ubus_complete_deferred_request(ctx, &req->req, 0); + req->fd = fds[1]; + + req->timeout.cb = test_hello_fd_reply; + test_hello_fd_reply(t); +} + +static int test_hello(struct ubus_context *ctx, struct ubus_object *obj, + struct ubus_request_data *req, const char *method, + struct blob_attr *msg) +{ + struct hello_request *hreq; + struct blob_attr *tb[__HELLO_MAX]; + const char *format = "%s received a message: %s"; + const char *msgstr = "(unknown)"; + + blobmsg_parse(hello_policy, ARRAY_SIZE(hello_policy), tb, blob_data(msg), blob_len(msg)); + + if (tb[HELLO_MSG]) + msgstr = blobmsg_data(tb[HELLO_MSG]); + + hreq = calloc(1, sizeof(*hreq) + strlen(format) + strlen(obj->name) + strlen(msgstr) + 1); + if (!hreq) + return UBUS_STATUS_UNKNOWN_ERROR; + + sprintf(hreq->data, format, obj->name, msgstr); + ubus_defer_request(ctx, req, &hreq->req); + hreq->timeout.cb = test_hello_reply; + uloop_timeout_set(&hreq->timeout, 1000); + + return 0; +} + +enum { + WATCH_ID, + WATCH_COUNTER, + __WATCH_MAX +}; + +static const struct blobmsg_policy watch_policy[__WATCH_MAX] = { + [WATCH_ID] = { .name = "id", .type = BLOBMSG_TYPE_INT32 }, + [WATCH_COUNTER] = { .name = "counter", .type = BLOBMSG_TYPE_INT32 }, +}; + +static void +test_handle_remove(struct ubus_context *ctx, struct ubus_subscriber *s, + uint32_t id) +{ + fprintf(stderr, "Object %08x went away\n", id); +} + +static int +test_notify(struct ubus_context *ctx, struct ubus_object *obj, + struct ubus_request_data *req, const char *method, + struct blob_attr *msg) +{ +#if 0 + char *str; + + str = blobmsg_format_json(msg, true); + fprintf(stderr, "Received notification '%s': %s\n", method, str); + free(str); +#endif + + return 0; +} + +static int test_watch(struct ubus_context *ctx, struct ubus_object *obj, + struct ubus_request_data *req, const char *method, + struct blob_attr *msg) +{ + struct blob_attr *tb[__WATCH_MAX]; + int ret; + + blobmsg_parse(watch_policy, __WATCH_MAX, tb, blob_data(msg), blob_len(msg)); + if (!tb[WATCH_ID]) + return UBUS_STATUS_INVALID_ARGUMENT; + + test_event.remove_cb = test_handle_remove; + test_event.cb = test_notify; + ret = ubus_subscribe(ctx, &test_event, blobmsg_get_u32(tb[WATCH_ID])); + fprintf(stderr, "Watching object %08x: %s\n", blobmsg_get_u32(tb[WATCH_ID]), ubus_strerror(ret)); + return ret; +} + +enum { + COUNT_TO, + COUNT_STRING, + __COUNT_MAX +}; + +static const struct blobmsg_policy count_policy[__COUNT_MAX] = { + [COUNT_TO] = { .name = "to", .type = BLOBMSG_TYPE_INT32 }, + [COUNT_STRING] = { .name = "string", .type = BLOBMSG_TYPE_STRING }, +}; + +static int test_count(struct ubus_context *ctx, struct ubus_object *obj, + struct ubus_request_data *req, const char *method, + struct blob_attr *msg) +{ + struct blob_attr *tb[__COUNT_MAX]; + char *s1, *s2; + uint32_t num; + + blobmsg_parse(count_policy, __COUNT_MAX, tb, blob_data(msg), blob_len(msg)); + if (!tb[COUNT_TO] || !tb[COUNT_STRING]) + return UBUS_STATUS_INVALID_ARGUMENT; + + num = blobmsg_get_u32(tb[COUNT_TO]); + s1 = blobmsg_get_string(tb[COUNT_STRING]); + s2 = count_to_number(num); + if (!s1 || !s2) { + free(s2); + return UBUS_STATUS_UNKNOWN_ERROR; + } + blob_buf_init(&b, 0); + blobmsg_add_u32(&b, "rc", strcmp(s1, s2)); + ubus_send_reply(ctx, req, b.head); + free(s2); + + return 0; +} + +static const struct ubus_method test_methods[] = { + UBUS_METHOD("hello", test_hello, hello_policy), + UBUS_METHOD("watch", test_watch, watch_policy), + UBUS_METHOD("count", test_count, count_policy), +}; + +static struct ubus_object_type test_object_type = + UBUS_OBJECT_TYPE("test", test_methods); + +static struct ubus_object test_object = { + .name = "test", + .type = &test_object_type, + .methods = test_methods, + .n_methods = ARRAY_SIZE(test_methods), +}; + +static void server_main(void) +{ + int ret; + + ret = ubus_add_object(ctx, &test_object); + if (ret) + fprintf(stderr, "Failed to add object: %s\n", ubus_strerror(ret)); + + ret = ubus_register_subscriber(ctx, &test_event); + if (ret) + fprintf(stderr, "Failed to add watch handler: %s\n", ubus_strerror(ret)); + + uloop_run(); +} + +int main(int argc, char **argv) +{ + const char *ubus_socket = NULL; + int ch; + + while ((ch = getopt(argc, argv, "cs:")) != -1) { + switch (ch) { + case 's': + ubus_socket = optarg; + break; + default: + break; + } + } + + argc -= optind; + argv += optind; + + uloop_init(); + signal(SIGPIPE, SIG_IGN); + + ctx = ubus_connect(ubus_socket); + if (!ctx) { + fprintf(stderr, "Failed to connect to ubus\n"); + return -1; + } + + ubus_add_uloop(ctx); + + server_main(); + + ubus_free(ctx); + uloop_done(); + + return 0; +} diff --git a/src/3P/ubus/libubus-acl.c b/src/3P/ubus/libubus-acl.c new file mode 100644 index 00000000..0274520f --- /dev/null +++ b/src/3P/ubus/libubus-acl.c @@ -0,0 +1,153 @@ +/* + * Copyright (C) 2015 John Cripin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include + +#include +#include + +#include "libubus.h" +#include + +static struct ubus_event_handler acl_event; +static struct ubus_request acl_req; +static struct blob_attr *acl_blob; + +static int acl_cmp(const void *k1, const void *k2, void *ptr) +{ + const struct ubus_acl_key *key1 = k1; + const struct ubus_acl_key *key2 = k2; + int ret = 0; + + if (key1->user && key2->user) + ret = strcmp(key1->user, key2->user); + if (ret) + return ret; + + if (key1->group && key2->group) + ret = strcmp(key1->group, key2->group); + if (ret) + return ret; + + return strcmp(key1->object, key2->object); +} + +AVL_TREE(acl_objects, acl_cmp, true, NULL); + +enum { + ACL_OBJ_OBJECT, + ACL_OBJ_USER, + ACL_OBJ_GROUP, + ACL_OBJ_ACL, + __ACL_OBJ_MAX +}; + +static const struct blobmsg_policy acl_obj_policy[__ACL_OBJ_MAX] = { + [ACL_OBJ_OBJECT] = { .name = "obj", .type = BLOBMSG_TYPE_STRING }, + [ACL_OBJ_USER] = { .name = "user", .type = BLOBMSG_TYPE_STRING }, + [ACL_OBJ_GROUP] = { .name = "group", .type = BLOBMSG_TYPE_STRING }, + [ACL_OBJ_ACL] = { .name = "acl", .type = BLOBMSG_TYPE_TABLE }, +}; + +static void +acl_add(struct blob_attr *obj) +{ + struct blob_attr *tb[__ACL_OBJ_MAX]; + struct acl_object *acl; + + blobmsg_parse(acl_obj_policy, __ACL_OBJ_MAX, tb, blobmsg_data(obj), + blobmsg_data_len(obj)); + + if (!tb[ACL_OBJ_OBJECT] || !tb[ACL_OBJ_ACL]) + return; + + if (!tb[ACL_OBJ_USER] && !tb[ACL_OBJ_GROUP]) + return; + + acl = calloc(1, sizeof(*acl)); + if (!acl) + return; + + acl->avl.key = &acl->key; + acl->key.object = blobmsg_get_string(tb[ACL_OBJ_OBJECT]); + acl->key.user = blobmsg_get_string(tb[ACL_OBJ_USER]); + acl->key.group = blobmsg_get_string(tb[ACL_OBJ_GROUP]); + acl->acl = tb[ACL_OBJ_ACL]; + avl_insert(&acl_objects, &acl->avl); +} + +enum { + ACL_POLICY_SEQ, + ACL_POLICY_ACL, + __ACL_POLICY_MAX +}; + +static const struct blobmsg_policy acl_policy[__ACL_POLICY_MAX] = { + [ACL_POLICY_SEQ] = { .name = "seq", .type = BLOBMSG_TYPE_INT32 }, + [ACL_POLICY_ACL] = { .name = "acl", .type = BLOBMSG_TYPE_ARRAY }, +}; + +static void acl_recv_cb(struct ubus_request *req, + int type, struct blob_attr *msg) +{ + struct blob_attr *tb[__ACL_POLICY_MAX]; + struct blob_attr *cur; + int rem; + + if (acl_blob) { + struct acl_object *p, *q; + + avl_for_each_element_safe(&acl_objects, p, avl, q) { + avl_delete(&acl_objects, &p->avl); + free(p); + } + free(acl_blob); + } + acl_blob = blob_memdup(msg); + blobmsg_parse(acl_policy, __ACL_POLICY_MAX, tb, blobmsg_data(msg), + blobmsg_data_len(msg)); + + if (!tb[ACL_POLICY_SEQ] && !tb[ACL_POLICY_ACL]) + return; + + blobmsg_for_each_attr(cur, tb[ACL_POLICY_ACL], rem) + acl_add(cur); +} + +static void acl_query(struct ubus_context *ctx) +{ + ubus_invoke_async(ctx, UBUS_SYSTEM_OBJECT_ACL, "query", NULL, &acl_req); + acl_req.data_cb = acl_recv_cb; + ubus_complete_request_async(ctx, &acl_req); +} + +static void acl_subscribe_cb(struct ubus_context *ctx, struct ubus_event_handler *ev, + const char *type, struct blob_attr *msg) +{ + if (strcmp(type, "ubus.acl.sequence")) + return; + acl_query(ctx); +} + +int ubus_register_acl(struct ubus_context *ctx) +{ + int ret; + + acl_event.cb = acl_subscribe_cb; + + ret = ubus_register_event_handler(ctx, &acl_event, "ubus.acl.sequence"); + if (!ret) + acl_query(ctx); + + return ret; +} diff --git a/src/3P/ubus/libubus-internal.h b/src/3P/ubus/libubus-internal.h new file mode 100644 index 00000000..f62edc38 --- /dev/null +++ b/src/3P/ubus/libubus-internal.h @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2011-2014 Felix Fietkau + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __LIBUBUS_IO_H +#define __LIBUBUS_IO_H + +extern struct blob_buf b; +extern const struct ubus_method watch_method; + +struct blob_attr **ubus_parse_msg(struct blob_attr *msg); +void ubus_handle_data(struct uloop_fd *u, unsigned int events); +int ubus_send_msg(struct ubus_context *ctx, uint32_t seq, + struct blob_attr *msg, int cmd, uint32_t peer, int fd); +void ubus_process_msg(struct ubus_context *ctx, struct ubus_msghdr_buf *buf, int fd); +int __hidden ubus_start_request(struct ubus_context *ctx, struct ubus_request *req, + struct blob_attr *msg, int cmd, uint32_t peer); +void ubus_process_obj_msg(struct ubus_context *ctx, struct ubus_msghdr_buf *buf); +void ubus_process_req_msg(struct ubus_context *ctx, struct ubus_msghdr_buf *buf, int fd); +void __hidden ubus_poll_data(struct ubus_context *ctx, int timeout); + + +#endif diff --git a/src/3P/ubus/libubus-io.c b/src/3P/ubus/libubus-io.c new file mode 100644 index 00000000..8a4f26a6 --- /dev/null +++ b/src/3P/ubus/libubus-io.c @@ -0,0 +1,412 @@ +/* + * Copyright (C) 2011-2014 Felix Fietkau + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +//#define _GNU_SOURCE +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include "libubus.h" +#include "libubus-internal.h" + +#define STATIC_IOV(_var) { .iov_base = (char *) &(_var), .iov_len = sizeof(_var) } + +#define UBUS_MSGBUF_REDUCTION_INTERVAL 16 + +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 }, + [UBUS_ATTR_METHOD] = { .type = BLOB_ATTR_STRING }, + [UBUS_ATTR_ACTIVE] = { .type = BLOB_ATTR_INT8 }, + [UBUS_ATTR_NO_REPLY] = { .type = BLOB_ATTR_INT8 }, + [UBUS_ATTR_SUBSCRIBERS] = { .type = BLOB_ATTR_NESTED }, +}; + +static struct blob_attr *attrbuf[UBUS_ATTR_MAX]; + +__hidden struct blob_attr **ubus_parse_msg(struct blob_attr *msg) +{ + blob_parse(msg, attrbuf, ubus_policy, UBUS_ATTR_MAX); + return attrbuf; +} + +static void wait_data(int fd, bool write) +{ + struct pollfd pfd = { .fd = fd }; + + pfd.events = write ? POLLOUT : POLLIN; + poll(&pfd, 1, -1); +} + +static int writev_retry(int fd, struct iovec *iov, int iov_len, int sock_fd) +{ + static struct { + struct cmsghdr h; + int fd; + } fd_buf = { + .h = { + .cmsg_len = sizeof(fd_buf), + .cmsg_level = SOL_SOCKET, + .cmsg_type = SCM_RIGHTS, + } + }; + struct msghdr msghdr = { + .msg_iov = iov, + .msg_iovlen = iov_len, + .msg_control = &fd_buf, + .msg_controllen = sizeof(fd_buf), + }; + int len = 0; + + do { + int cur_len; + + if (sock_fd < 0) { + msghdr.msg_control = NULL; + msghdr.msg_controllen = 0; + } else { + fd_buf.fd = sock_fd; + } + + cur_len = sendmsg(fd, &msghdr, 0); + if (cur_len < 0) { + switch(errno) { + case EAGAIN: + wait_data(fd, true); + break; + case EINTR: + break; + default: + return -1; + } + continue; + } + + if (len > 0) + sock_fd = -1; + + len += cur_len; + while (cur_len >= iov->iov_len) { + cur_len -= iov->iov_len; + iov_len--; + iov++; + if (!iov_len) + return len; + } + iov->iov_base += cur_len; + iov->iov_len -= cur_len; + msghdr.msg_iov = iov; + msghdr.msg_iovlen = iov_len; + } while (1); + + /* Should never reach here */ + return -1; +} + +int __hidden ubus_send_msg(struct ubus_context *ctx, uint32_t seq, + struct blob_attr *msg, int cmd, uint32_t peer, int fd) +{ + struct ubus_msghdr hdr; + struct iovec iov[2] = { + STATIC_IOV(hdr) + }; + int ret; + + hdr.version = 0; + hdr.type = cmd; + hdr.seq = cpu_to_be16(seq); + hdr.peer = cpu_to_be32(peer); + + if (!msg) { + blob_buf_init(&b, 0); + msg = b.head; + } + + iov[1].iov_base = (char *) msg; + iov[1].iov_len = blob_raw_len(msg); + + ret = writev_retry(ctx->sock.fd, iov, ARRAY_SIZE(iov), fd); + if (ret < 0) + ctx->sock.eof = true; + + if (fd >= 0) + close(fd); + + return ret; +} + +static int recv_retry(int fd, struct iovec *iov, bool wait, int *recv_fd) +{ + int bytes, total = 0; + static struct { + struct cmsghdr h; + int fd; + } fd_buf = { + .h = { + .cmsg_type = SCM_RIGHTS, + .cmsg_level = SOL_SOCKET, + .cmsg_len = sizeof(fd_buf), + }, + }; + struct msghdr msghdr = { + .msg_iov = iov, + .msg_iovlen = 1, + }; + + while (iov->iov_len > 0) { + if (wait) + wait_data(fd, false); + + if (recv_fd) { + msghdr.msg_control = &fd_buf; + msghdr.msg_controllen = sizeof(fd_buf); + } else { + msghdr.msg_control = NULL; + msghdr.msg_controllen = 0; + } + + fd_buf.fd = -1; + bytes = recvmsg(fd, &msghdr, 0); + if (!bytes) + return -1; + + if (bytes < 0) { + bytes = 0; + if (uloop_cancelled) + return 0; + if (errno == EINTR) + continue; + + if (errno != EAGAIN) + return -1; + } + if (!wait && !bytes) + return 0; + + if (recv_fd) + *recv_fd = fd_buf.fd; + + recv_fd = NULL; + + wait = true; + iov->iov_len -= bytes; + iov->iov_base += bytes; + total += bytes; + } + + return total; +} + +static bool ubus_validate_hdr(struct ubus_msghdr *hdr) +{ + struct blob_attr *data = (struct blob_attr *) (hdr + 1); + + if (hdr->version != 0) + return false; + + if (blob_raw_len(data) < sizeof(*data)) + return false; + + if (blob_pad_len(data) > UBUS_MAX_MSGLEN) + return false; + + return true; +} + +static bool alloc_msg_buf(struct ubus_context *ctx, int len) +{ + void *ptr; + int buf_len = ctx->msgbuf_data_len; + int rem; + + if (!ctx->msgbuf.data) + buf_len = 0; + + rem = (len % UBUS_MSG_CHUNK_SIZE); + if (rem > 0) + len += UBUS_MSG_CHUNK_SIZE - rem; + + if (len < buf_len && + ++ctx->msgbuf_reduction_counter > UBUS_MSGBUF_REDUCTION_INTERVAL) { + ctx->msgbuf_reduction_counter = 0; + buf_len = 0; + } + + if (len <= buf_len) + return true; + + ptr = realloc(ctx->msgbuf.data, len); + if (!ptr) + return false; + + ctx->msgbuf.data = ptr; + ctx->msgbuf_data_len = len; + return true; +} + +static bool get_next_msg(struct ubus_context *ctx, int *recv_fd) +{ + struct { + struct ubus_msghdr hdr; + struct blob_attr data; + } hdrbuf; + struct iovec iov = STATIC_IOV(hdrbuf); + int len; + int r; + + /* receive header + start attribute */ + r = recv_retry(ctx->sock.fd, &iov, false, recv_fd); + if (r <= 0) { + if (r < 0) + ctx->sock.eof = true; + + return false; + } + + hdrbuf.hdr.seq = be16_to_cpu(hdrbuf.hdr.seq); + hdrbuf.hdr.peer = be32_to_cpu(hdrbuf.hdr.peer); + + if (!ubus_validate_hdr(&hdrbuf.hdr)) + return false; + + len = blob_raw_len(&hdrbuf.data); + if (!alloc_msg_buf(ctx, len)) + return false; + + memcpy(&ctx->msgbuf.hdr, &hdrbuf.hdr, sizeof(hdrbuf.hdr)); + memcpy(ctx->msgbuf.data, &hdrbuf.data, sizeof(hdrbuf.data)); + + iov.iov_base = (char *)ctx->msgbuf.data + sizeof(hdrbuf.data); + iov.iov_len = blob_len(ctx->msgbuf.data); + if (iov.iov_len > 0 && + recv_retry(ctx->sock.fd, &iov, true, NULL) <= 0) + return false; + + return true; +} + +void __hidden ubus_handle_data(struct uloop_fd *u, unsigned int events) +{ + struct ubus_context *ctx = container_of(u, struct ubus_context, sock); + int recv_fd = -1; + + while (get_next_msg(ctx, &recv_fd)) { + ubus_process_msg(ctx, &ctx->msgbuf, recv_fd); + if (uloop_cancelled) + break; + } + + if (u->eof) + ctx->connection_lost(ctx); +} + +void __hidden ubus_poll_data(struct ubus_context *ctx, int timeout) +{ + struct pollfd pfd = { + .fd = ctx->sock.fd, + .events = POLLIN | POLLERR, + }; + + poll(&pfd, 1, timeout ? timeout : -1); + ubus_handle_data(&ctx->sock, ULOOP_READ); +} + +static void +ubus_refresh_state(struct ubus_context *ctx) +{ + struct ubus_object *obj, *tmp; + struct ubus_object **objs; + int n, i = 0; + + /* clear all type IDs, they need to be registered again */ + avl_for_each_element(&ctx->objects, obj, avl) + if (obj->type) + obj->type->id = 0; + + /* push out all objects again */ + objs = alloca(ctx->objects.count * sizeof(*objs)); + avl_remove_all_elements(&ctx->objects, obj, avl, tmp) { + objs[i++] = obj; + obj->id = 0; + } + + for (n = i, i = 0; i < n; i++) + ubus_add_object(ctx, objs[i]); +} + +int ubus_reconnect(struct ubus_context *ctx, const char *path) +{ + struct { + struct ubus_msghdr hdr; + struct blob_attr data; + } hdr; + struct blob_attr *buf; + int ret = UBUS_STATUS_UNKNOWN_ERROR; + + if (!path) + path = UBUS_UNIX_SOCKET; + + if (ctx->sock.fd >= 0) { + if (ctx->sock.registered) + uloop_fd_delete(&ctx->sock); + + close(ctx->sock.fd); + } + + ctx->sock.fd = usock(USOCK_UNIX, path, NULL); + if (ctx->sock.fd < 0) + return UBUS_STATUS_CONNECTION_FAILED; + + if (read(ctx->sock.fd, &hdr, sizeof(hdr)) != sizeof(hdr)) + goto out_close; + + if (!ubus_validate_hdr(&hdr.hdr)) + goto out_close; + + if (hdr.hdr.type != UBUS_MSG_HELLO) + goto out_close; + + buf = calloc(1, blob_raw_len(&hdr.data)); + if (!buf) + goto out_close; + + memcpy(buf, &hdr.data, sizeof(hdr.data)); + if (read(ctx->sock.fd, blob_data(buf), blob_len(buf)) != blob_len(buf)) + goto out_free; + + ctx->local_id = hdr.hdr.peer; + if (!ctx->local_id) + goto out_free; + + ret = UBUS_STATUS_OK; + fcntl(ctx->sock.fd, F_SETFL, fcntl(ctx->sock.fd, F_GETFL) | O_NONBLOCK | O_CLOEXEC); + + ubus_refresh_state(ctx); + +out_free: + free(buf); +out_close: + if (ret) + close(ctx->sock.fd); + + return ret; +} diff --git a/src/3P/ubus/libubus-obj.c b/src/3P/ubus/libubus-obj.c new file mode 100644 index 00000000..990d04bd --- /dev/null +++ b/src/3P/ubus/libubus-obj.c @@ -0,0 +1,261 @@ +/* + * Copyright (C) 2011-2012 Felix Fietkau + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "libubus.h" +#include "libubus-internal.h" + +static void +ubus_process_unsubscribe(struct ubus_context *ctx, struct ubus_msghdr *hdr, + struct ubus_object *obj, struct blob_attr **attrbuf) +{ + struct ubus_subscriber *s; + + if (!obj || !attrbuf[UBUS_ATTR_TARGET]) + return; + + if (obj->methods != &watch_method) + return; + + s = container_of(obj, struct ubus_subscriber, obj); + if (s->remove_cb) + s->remove_cb(ctx, s, blob_get_u32(attrbuf[UBUS_ATTR_TARGET])); +} + +static void +ubus_process_notify(struct ubus_context *ctx, struct ubus_msghdr *hdr, + struct ubus_object *obj, struct blob_attr **attrbuf) +{ + if (!obj || !attrbuf[UBUS_ATTR_ACTIVE]) + return; + + obj->has_subscribers = blob_get_u8(attrbuf[UBUS_ATTR_ACTIVE]); + if (obj->subscribe_cb) + obj->subscribe_cb(ctx, obj); +} +static void +ubus_process_invoke(struct ubus_context *ctx, struct ubus_msghdr *hdr, + struct ubus_object *obj, struct blob_attr **attrbuf) +{ + struct ubus_request_data req = { + .fd = -1, + }; + int method; + int ret; + bool no_reply = false; + + if (!obj) { + ret = UBUS_STATUS_NOT_FOUND; + goto send; + } + + if (!attrbuf[UBUS_ATTR_METHOD]) { + ret = UBUS_STATUS_INVALID_ARGUMENT; + goto send; + } + + if (attrbuf[UBUS_ATTR_NO_REPLY]) + no_reply = blob_get_int8(attrbuf[UBUS_ATTR_NO_REPLY]); + + req.peer = hdr->peer; + req.seq = hdr->seq; + req.object = obj->id; + if (attrbuf[UBUS_ATTR_USER] && attrbuf[UBUS_ATTR_GROUP]) { + req.acl.user = blobmsg_get_string(attrbuf[UBUS_ATTR_USER]); + req.acl.group = blobmsg_get_string(attrbuf[UBUS_ATTR_GROUP]); + req.acl.object = obj->name; + } + for (method = 0; method < obj->n_methods; method++) + if (!obj->methods[method].name || + !strcmp(obj->methods[method].name, + blob_data(attrbuf[UBUS_ATTR_METHOD]))) + goto found; + + /* not found */ + ret = UBUS_STATUS_METHOD_NOT_FOUND; + goto send; + +found: + ret = obj->methods[method].handler(ctx, obj, &req, + blob_data(attrbuf[UBUS_ATTR_METHOD]), + attrbuf[UBUS_ATTR_DATA]); + if (req.deferred || no_reply) + return; + +send: + ubus_complete_deferred_request(ctx, &req, ret); +} + +void __hidden ubus_process_obj_msg(struct ubus_context *ctx, struct ubus_msghdr_buf *buf) +{ + void (*cb)(struct ubus_context *, struct ubus_msghdr *, + struct ubus_object *, struct blob_attr **); + struct ubus_msghdr *hdr = &buf->hdr; + struct blob_attr **attrbuf; + struct ubus_object *obj; + uint32_t objid; + void *prev_data = NULL; + + attrbuf = ubus_parse_msg(buf->data); + if (!attrbuf[UBUS_ATTR_OBJID]) + return; + + objid = blob_get_u32(attrbuf[UBUS_ATTR_OBJID]); + obj = avl_find_element(&ctx->objects, &objid, obj, avl); + + switch (hdr->type) { + case UBUS_MSG_INVOKE: + cb = ubus_process_invoke; + break; + case UBUS_MSG_UNSUBSCRIBE: + cb = ubus_process_unsubscribe; + break; + case UBUS_MSG_NOTIFY: + cb = ubus_process_notify; + break; + default: + return; + } + + if (buf == &ctx->msgbuf) { + prev_data = buf->data; + buf->data = NULL; + } + + cb(ctx, hdr, obj, attrbuf); + + if (prev_data) { + if (buf->data) + free(prev_data); + else + buf->data = prev_data; + } +} + +static void ubus_add_object_cb(struct ubus_request *req, int type, struct blob_attr *msg) +{ + struct ubus_object *obj = req->priv; + struct blob_attr **attrbuf = ubus_parse_msg(msg); + + if (!attrbuf[UBUS_ATTR_OBJID]) + return; + + obj->id = blob_get_u32(attrbuf[UBUS_ATTR_OBJID]); + + if (attrbuf[UBUS_ATTR_OBJTYPE]) + obj->type->id = blob_get_u32(attrbuf[UBUS_ATTR_OBJTYPE]); + + obj->avl.key = &obj->id; + avl_insert(&req->ctx->objects, &obj->avl); +} + +static void ubus_push_method_data(const struct ubus_method *m) +{ + void *mtbl; + int i; + + mtbl = blobmsg_open_table(&b, m->name); + + for (i = 0; i < m->n_policy; i++) { + if (m->mask && !(m->mask & (1 << i))) + continue; + + blobmsg_add_u32(&b, m->policy[i].name, m->policy[i].type); + } + + blobmsg_close_table(&b, mtbl); +} + +static bool ubus_push_object_type(const struct ubus_object_type *type) +{ + void *s; + int i; + + s = blob_nest_start(&b, UBUS_ATTR_SIGNATURE); + + for (i = 0; i < type->n_methods; i++) + ubus_push_method_data(&type->methods[i]); + + blob_nest_end(&b, s); + + return true; +} + +int ubus_add_object(struct ubus_context *ctx, struct ubus_object *obj) +{ + struct ubus_request req; + int ret; + + blob_buf_init(&b, 0); + + if (obj->name && obj->type) { + blob_put_string(&b, UBUS_ATTR_OBJPATH, obj->name); + + 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; + } + + if (ubus_start_request(ctx, &req, b.head, UBUS_MSG_ADD_OBJECT, 0) < 0) + return UBUS_STATUS_INVALID_ARGUMENT; + + req.raw_data_cb = ubus_add_object_cb; + req.priv = obj; + ret = ubus_complete_request(ctx, &req, 0); + if (ret) + return ret; + + if (!obj->id) + return UBUS_STATUS_NO_DATA; + + return 0; +} + +static void ubus_remove_object_cb(struct ubus_request *req, int type, struct blob_attr *msg) +{ + struct ubus_object *obj = req->priv; + struct blob_attr **attrbuf = ubus_parse_msg(msg); + + if (!attrbuf[UBUS_ATTR_OBJID]) + return; + + obj->id = 0; + + if (attrbuf[UBUS_ATTR_OBJTYPE] && obj->type) + obj->type->id = 0; + + avl_delete(&req->ctx->objects, &obj->avl); +} + +int ubus_remove_object(struct ubus_context *ctx, struct ubus_object *obj) +{ + struct ubus_request req; + int ret; + + blob_buf_init(&b, 0); + blob_put_int32(&b, UBUS_ATTR_OBJID, obj->id); + + if (ubus_start_request(ctx, &req, b.head, UBUS_MSG_REMOVE_OBJECT, 0) < 0) + return UBUS_STATUS_INVALID_ARGUMENT; + + req.raw_data_cb = ubus_remove_object_cb; + req.priv = obj; + ret = ubus_complete_request(ctx, &req, 0); + if (ret) + return ret; + + if (obj->id) + return UBUS_STATUS_NO_DATA; + + return 0; +} diff --git a/src/3P/ubus/libubus-req.c b/src/3P/ubus/libubus-req.c new file mode 100644 index 00000000..416adaba --- /dev/null +++ b/src/3P/ubus/libubus-req.c @@ -0,0 +1,478 @@ +/* + * Copyright (C) 2011-2014 Felix Fietkau + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include "libubus.h" +#include "libubus-internal.h" + +struct ubus_pending_data { + struct list_head list; + int type; + struct blob_attr data[]; +}; + +static void req_data_cb(struct ubus_request *req, int type, struct blob_attr *data) +{ + struct blob_attr **attr; + + if (req->raw_data_cb) + req->raw_data_cb(req, type, data); + + if (!req->data_cb) + return; + + attr = ubus_parse_msg(data); + req->data_cb(req, type, attr[UBUS_ATTR_DATA]); +} + +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); + if (!req->cancelled) + req_data_cb(req, data->type, data->data); + free(data); + } +} + +int __hidden ubus_start_request(struct ubus_context *ctx, struct ubus_request *req, + struct blob_attr *msg, int cmd, uint32_t peer) +{ + memset(req, 0, sizeof(*req)); + + if (msg && blob_pad_len(msg) > UBUS_MAX_MSGLEN) + return -1; + + INIT_LIST_HEAD(&req->list); + INIT_LIST_HEAD(&req->pending); + req->ctx = ctx; + req->peer = peer; + req->seq = ++ctx->request_seq; + return ubus_send_msg(ctx, req->seq, msg, cmd, peer, -1); +} + +void ubus_abort_request(struct ubus_context *ctx, struct ubus_request *req) +{ + if (list_empty(&req->list)) + return; + + req->cancelled = true; + __ubus_process_req_data(req); + list_del_init(&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_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 void +ubus_set_req_status(struct ubus_request *req, int ret) +{ + if (!list_empty(&req->list)) + list_del_init(&req->list); + + req->status_msg = true; + req->status_code = ret; + if (!req->blocked) + ubus_req_complete_cb(req); +} + +static void ubus_sync_req_cb(struct ubus_request *req, int ret) +{ + req->status_msg = true; + req->status_code = ret; + uloop_end(); +} + +static int64_t get_time_msec(void) +{ + struct timespec ts; + int64_t val; + + clock_gettime(CLOCK_MONOTONIC, &ts); + val = (int64_t) ts.tv_sec * 1000LL; + val += ts.tv_nsec / 1000000LL; + return val; +} + +int ubus_complete_request(struct ubus_context *ctx, struct ubus_request *req, + int req_timeout) +{ + ubus_complete_handler_t complete_cb = req->complete_cb; + bool registered = ctx->sock.registered; + int status = UBUS_STATUS_NO_DATA; + int64_t timeout = 0, time_end = 0; + + if (!registered) { + uloop_init(); + ubus_add_uloop(ctx); + } + + if (req_timeout) + time_end = get_time_msec() + req_timeout; + + ubus_complete_request_async(ctx, req); + req->complete_cb = ubus_sync_req_cb; + + ctx->stack_depth++; + while (!req->status_msg) { + bool cancelled = uloop_cancelled; + + uloop_cancelled = false; + if (req_timeout) { + timeout = time_end - get_time_msec(); + if (timeout <= 0) { + ubus_set_req_status(req, UBUS_STATUS_TIMEOUT); + uloop_cancelled = cancelled; + break; + } + } + ubus_poll_data(ctx, (unsigned int) timeout); + + uloop_cancelled = cancelled; + if (ctx->sock.eof) { + ubus_set_req_status(req, UBUS_STATUS_CONNECTION_FAILED); + break; + } + } + ctx->stack_depth--; + if (ctx->stack_depth) + uloop_cancelled = true; + + if (req->status_msg) + status = req->status_code; + + req->complete_cb = complete_cb; + if (req->complete_cb) + req->complete_cb(req, status); + + if (!registered) { + uloop_fd_delete(&ctx->sock); + + if (!ctx->stack_depth) + ctx->pending_timer.cb(&ctx->pending_timer); + } + + return status; +} + +void ubus_complete_deferred_request(struct ubus_context *ctx, struct ubus_request_data *req, int ret) +{ + blob_buf_init(&b, 0); + blob_put_int32(&b, UBUS_ATTR_STATUS, ret); + blob_put_int32(&b, UBUS_ATTR_OBJID, req->object); + ubus_send_msg(ctx, req->seq, b.head, UBUS_MSG_STATUS, req->peer, req->fd); +} + +int ubus_send_reply(struct ubus_context *ctx, struct ubus_request_data *req, + struct blob_attr *msg) +{ + int ret; + + blob_buf_init(&b, 0); + blob_put_int32(&b, UBUS_ATTR_OBJID, req->object); + blob_put(&b, UBUS_ATTR_DATA, blob_data(msg), blob_len(msg)); + ret = ubus_send_msg(ctx, req->seq, b.head, UBUS_MSG_DATA, req->peer, -1); + if (ret < 0) + return UBUS_STATUS_NO_DATA; + + return 0; +} + +int 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); + if (msg) + blob_put(&b, UBUS_ATTR_DATA, blob_data(msg), blob_len(msg)); + + if (ubus_start_request(ctx, req, b.head, UBUS_MSG_INVOKE, obj) < 0) + return UBUS_STATUS_INVALID_ARGUMENT; + + return 0; +} + +int ubus_invoke(struct ubus_context *ctx, uint32_t obj, const char *method, + struct blob_attr *msg, ubus_data_handler_t cb, void *priv, + int timeout) +{ + struct ubus_request req; + int rc; + + rc = ubus_invoke_async(ctx, obj, method, msg, &req); + if (rc) + return rc; + + req.data_cb = cb; + req.priv = priv; + return ubus_complete_request(ctx, &req, timeout); +} + +static void +ubus_notify_complete_cb(struct ubus_request *req, int ret) +{ + struct ubus_notify_request *nreq; + + nreq = container_of(req, struct ubus_notify_request, req); + if (!nreq->complete_cb) + return; + + nreq->complete_cb(nreq, 0, 0); +} + +static int +__ubus_notify_async(struct ubus_context *ctx, struct ubus_object *obj, + const char *type, struct blob_attr *msg, + struct ubus_notify_request *req, bool reply) +{ + memset(req, 0, sizeof(*req)); + + blob_buf_init(&b, 0); + blob_put_int32(&b, UBUS_ATTR_OBJID, obj->id); + blob_put_string(&b, UBUS_ATTR_METHOD, type); + + if (!reply) + blob_put_int8(&b, UBUS_ATTR_NO_REPLY, true); + + if (msg) + blob_put(&b, UBUS_ATTR_DATA, blob_data(msg), blob_len(msg)); + + if (ubus_start_request(ctx, &req->req, b.head, UBUS_MSG_NOTIFY, obj->id) < 0) + return UBUS_STATUS_INVALID_ARGUMENT; + + /* wait for status message from ubusd first */ + req->req.notify = true; + req->pending = 1; + req->id[0] = obj->id; + req->req.complete_cb = ubus_notify_complete_cb; + + return 0; +} + +int ubus_notify_async(struct ubus_context *ctx, struct ubus_object *obj, + const char *type, struct blob_attr *msg, + struct ubus_notify_request *req) +{ + return __ubus_notify_async(ctx, obj, type, msg, req, true); +} + +int ubus_notify(struct ubus_context *ctx, struct ubus_object *obj, + const char *type, struct blob_attr *msg, int timeout) +{ + struct ubus_notify_request req; + int ret; + + ret = __ubus_notify_async(ctx, obj, type, msg, &req, timeout >= 0); + if (ret < 0) + return ret; + + if (timeout < 0) { + ubus_abort_request(ctx, &req.req); + return 0; + } + + return ubus_complete_request(ctx, &req.req, timeout); +} + +static bool ubus_get_status(struct ubus_msghdr_buf *buf, int *ret) +{ + struct blob_attr **attrbuf = ubus_parse_msg(buf->data); + + if (!attrbuf[UBUS_ATTR_STATUS]) + return false; + + *ret = blob_get_u32(attrbuf[UBUS_ATTR_STATUS]); + return true; +} + +static int +ubus_process_req_status(struct ubus_request *req, struct ubus_msghdr_buf *buf) +{ + int ret = UBUS_STATUS_INVALID_ARGUMENT; + + ubus_get_status(buf, &ret); + req->peer = buf->hdr.peer; + ubus_set_req_status(req, ret); + + return ret; +} + +static void +ubus_process_req_data(struct ubus_request *req, struct ubus_msghdr_buf *buf) +{ + struct ubus_pending_data *data; + int len; + + if (!req->blocked) { + req->blocked = true; + req_data_cb(req, buf->hdr.type, buf->data); + __ubus_process_req_data(req); + req->blocked = false; + + if (req->status_msg) + ubus_req_complete_cb(req); + + return; + } + + len = blob_raw_len(buf->data); + data = calloc(1, sizeof(*data) + len); + if (!data) + return; + + data->type = buf->hdr.type; + memcpy(data->data, buf->data, len); + list_add(&data->list, &req->pending); +} + +static int +ubus_find_notify_id(struct ubus_notify_request *n, uint32_t objid) +{ + uint32_t pending = n->pending; + int i; + + for (i = 0; pending; i++, pending >>= 1) { + if (!(pending & 1)) + continue; + + if (n->id[i] == objid) + return i; + } + + return -1; +} + +static struct ubus_request * +ubus_find_request(struct ubus_context *ctx, uint32_t seq, uint32_t peer, int *id) +{ + struct ubus_request *req; + + list_for_each_entry(req, &ctx->requests, list) { + struct ubus_notify_request *nreq; + nreq = container_of(req, struct ubus_notify_request, req); + + if (seq != req->seq) + continue; + + if (req->notify) { + if (!nreq->pending) + continue; + + *id = ubus_find_notify_id(nreq, peer); + if (*id < 0) + continue; + } else if (peer != req->peer) + continue; + + return req; + } + return NULL; +} + +static void ubus_process_notify_status(struct ubus_request *req, int id, struct ubus_msghdr_buf *buf) +{ + struct ubus_notify_request *nreq; + struct blob_attr **tb; + struct blob_attr *cur; + int rem, idx = 1; + int ret = 0; + + nreq = container_of(req, struct ubus_notify_request, req); + nreq->pending &= ~(1 << id); + + if (!id) { + /* first id: ubusd's status message with a list of ids */ + tb = ubus_parse_msg(buf->data); + if (tb[UBUS_ATTR_SUBSCRIBERS]) { + blob_for_each_attr(cur, tb[UBUS_ATTR_SUBSCRIBERS], rem) { + if (!blob_check_type(blob_data(cur), blob_len(cur), BLOB_ATTR_INT32)) + continue; + + nreq->pending |= (1 << idx); + nreq->id[idx] = blob_get_int32(cur); + idx++; + + if (idx == UBUS_MAX_NOTIFY_PEERS + 1) + break; + } + } + } else { + ubus_get_status(buf, &ret); + if (nreq->status_cb) + nreq->status_cb(nreq, id, ret); + } + + if (!nreq->pending) + ubus_set_req_status(req, 0); +} + +void __hidden ubus_process_req_msg(struct ubus_context *ctx, struct ubus_msghdr_buf *buf, int fd) +{ + struct ubus_msghdr *hdr = &buf->hdr; + struct ubus_request *req; + int id = -1; + + switch(hdr->type) { + case UBUS_MSG_STATUS: + req = ubus_find_request(ctx, hdr->seq, hdr->peer, &id); + if (!req) + break; + + if (fd >= 0) { + if (req->fd_cb) + req->fd_cb(req, fd); + else + close(fd); + } + + if (id >= 0) + ubus_process_notify_status(req, id, buf); + else + ubus_process_req_status(req, buf); + break; + + case UBUS_MSG_DATA: + req = ubus_find_request(ctx, hdr->seq, hdr->peer, &id); + if (req && (req->data_cb || req->raw_data_cb)) + ubus_process_req_data(req, buf); + break; + } +} + +int __ubus_monitor(struct ubus_context *ctx, const char *type) +{ + blob_buf_init(&b, 0); + return ubus_invoke(ctx, UBUS_SYSTEM_OBJECT_MONITOR, type, b.head, NULL, NULL, 1000); +} diff --git a/src/3P/ubus/libubus-sub.c b/src/3P/ubus/libubus-sub.c new file mode 100644 index 00000000..8793133a --- /dev/null +++ b/src/3P/ubus/libubus-sub.c @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2011-2012 Felix Fietkau + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "libubus.h" +#include "libubus-internal.h" + +static int ubus_subscriber_cb(struct ubus_context *ctx, struct ubus_object *obj, + struct ubus_request_data *req, + const char *method, struct blob_attr *msg) +{ + struct ubus_subscriber *s; + + s = container_of(obj, struct ubus_subscriber, obj); + if (s->cb) + return s->cb(ctx, obj, req, method, msg); + return 0; +} + +const struct ubus_method watch_method __hidden = { + .name = NULL, + .handler = ubus_subscriber_cb, +}; + +int ubus_register_subscriber(struct ubus_context *ctx, struct ubus_subscriber *s) +{ + struct ubus_object *obj = &s->obj; + + obj->methods = &watch_method; + obj->n_methods = 1; + + return ubus_add_object(ctx, obj); +} + +static int +__ubus_subscribe_request(struct ubus_context *ctx, struct ubus_object *obj, uint32_t id, int type) +{ + struct ubus_request req; + + blob_buf_init(&b, 0); + blob_put_int32(&b, UBUS_ATTR_OBJID, obj->id); + blob_put_int32(&b, UBUS_ATTR_TARGET, id); + + if (ubus_start_request(ctx, &req, b.head, type, 0) < 0) + return UBUS_STATUS_INVALID_ARGUMENT; + + return ubus_complete_request(ctx, &req, 0); + +} + +int ubus_subscribe(struct ubus_context *ctx, struct ubus_subscriber *obj, uint32_t id) +{ + return __ubus_subscribe_request(ctx, &obj->obj, id, UBUS_MSG_SUBSCRIBE); +} + +int ubus_unsubscribe(struct ubus_context *ctx, struct ubus_subscriber *obj, uint32_t id) +{ + return __ubus_subscribe_request(ctx, &obj->obj, id, UBUS_MSG_UNSUBSCRIBE); +} + diff --git a/src/3P/ubus/libubus.c b/src/3P/ubus/libubus.c new file mode 100644 index 00000000..076c5507 --- /dev/null +++ b/src/3P/ubus/libubus.c @@ -0,0 +1,373 @@ +/* + * Copyright (C) 2011-2014 Felix Fietkau + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include + +#include +#include + +#include "libubus.h" +#include "libubus-internal.h" +#include "ubusmsg.h" + +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_METHOD_NOT_FOUND] = "Method not found", + [UBUS_STATUS_NOT_FOUND] = "Not found", + [UBUS_STATUS_NO_DATA] = "No response", + [UBUS_STATUS_PERMISSION_DENIED] = "Permission denied", + [UBUS_STATUS_TIMEOUT] = "Request timed out", + [UBUS_STATUS_NOT_SUPPORTED] = "Operation not supported", + [UBUS_STATUS_UNKNOWN_ERROR] = "Unknown error", + [UBUS_STATUS_CONNECTION_FAILED] = "Connection failed", +}; + +struct blob_buf b __hidden = {}; + +struct ubus_pending_msg { + struct list_head list; + struct ubus_msghdr_buf hdr; +}; + +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; +} + +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; +} + +static void +ubus_queue_msg(struct ubus_context *ctx, struct ubus_msghdr_buf *buf) +{ + struct ubus_pending_msg *pending; + void *data; + + pending = calloc_a(sizeof(*pending), &data, blob_raw_len(buf->data)); + + pending->hdr.data = data; + memcpy(&pending->hdr.hdr, &buf->hdr, sizeof(buf->hdr)); + memcpy(data, buf->data, blob_raw_len(buf->data)); + list_add_tail(&pending->list, &ctx->pending); + if (ctx->sock.registered) + uloop_timeout_set(&ctx->pending_timer, 1); +} + +void __hidden +ubus_process_msg(struct ubus_context *ctx, struct ubus_msghdr_buf *buf, int fd) +{ + switch(buf->hdr.type) { + case UBUS_MSG_STATUS: + case UBUS_MSG_DATA: + ubus_process_req_msg(ctx, buf, fd); + break; + + case UBUS_MSG_INVOKE: + case UBUS_MSG_UNSUBSCRIBE: + case UBUS_MSG_NOTIFY: + if (ctx->stack_depth) { + ubus_queue_msg(ctx, buf); + break; + } + + ubus_process_obj_msg(ctx, buf); + break; + case UBUS_MSG_MONITOR: + if (ctx->monitor_cb) + ctx->monitor_cb(ctx, buf->hdr.seq, buf->data); + break; + } +} + +static void ubus_process_pending_msg(struct uloop_timeout *timeout) +{ + struct ubus_context *ctx = container_of(timeout, struct ubus_context, pending_timer); + struct ubus_pending_msg *pending; + + while (!ctx->stack_depth && !list_empty(&ctx->pending)) { + pending = list_first_entry(&ctx->pending, struct ubus_pending_msg, list); + list_del(&pending->list); + ubus_process_msg(ctx, &pending->hdr, -1); + free(pending); + } +} + +struct ubus_lookup_request { + struct ubus_request req; + ubus_lookup_handler_t cb; +}; + +static void ubus_lookup_cb(struct ubus_request *ureq, int type, struct blob_attr *msg) +{ + struct ubus_lookup_request *req; + struct ubus_object_data obj; + struct blob_attr **attr; + + req = container_of(ureq, struct ubus_lookup_request, req); + attr = ubus_parse_msg(msg); + + if (!attr[UBUS_ATTR_OBJID] || !attr[UBUS_ATTR_OBJPATH] || + !attr[UBUS_ATTR_OBJTYPE]) + return; + + memset(&obj, 0, sizeof(obj)); + obj.id = blob_get_u32(attr[UBUS_ATTR_OBJID]); + obj.path = blob_data(attr[UBUS_ATTR_OBJPATH]); + obj.type_id = blob_get_u32(attr[UBUS_ATTR_OBJTYPE]); + obj.signature = attr[UBUS_ATTR_SIGNATURE]; + req->cb(ureq->ctx, &obj, ureq->priv); +} + +int ubus_lookup(struct ubus_context *ctx, const char *path, + ubus_lookup_handler_t cb, void *priv) +{ + struct ubus_lookup_request lookup; + + blob_buf_init(&b, 0); + if (path) + blob_put_string(&b, UBUS_ATTR_OBJPATH, path); + + if (ubus_start_request(ctx, &lookup.req, b.head, UBUS_MSG_LOOKUP, 0) < 0) + return UBUS_STATUS_INVALID_ARGUMENT; + + lookup.req.raw_data_cb = ubus_lookup_cb; + lookup.req.priv = priv; + lookup.cb = cb; + return ubus_complete_request(ctx, &lookup.req, 0); +} + +static void ubus_lookup_id_cb(struct ubus_request *req, int type, struct blob_attr *msg) +{ + struct blob_attr **attr; + uint32_t *id = req->priv; + + attr = ubus_parse_msg(msg); + + if (!attr[UBUS_ATTR_OBJID]) + return; + + *id = blob_get_u32(attr[UBUS_ATTR_OBJID]); +} + +int ubus_lookup_id(struct ubus_context *ctx, const char *path, uint32_t *id) +{ + struct ubus_request req; + + blob_buf_init(&b, 0); + if (path) + blob_put_string(&b, UBUS_ATTR_OBJPATH, path); + + if (ubus_start_request(ctx, &req, b.head, UBUS_MSG_LOOKUP, 0) < 0) + return UBUS_STATUS_INVALID_ARGUMENT; + + req.raw_data_cb = ubus_lookup_id_cb; + req.priv = id; + + // Awox Remomve infinite timeout: return ubus_complete_request(ctx, &req, 0); + return ubus_complete_request(ctx, &req, 5000); +} + +static int ubus_event_cb(struct ubus_context *ctx, struct ubus_object *obj, + struct ubus_request_data *req, + const char *method, struct blob_attr *msg) +{ + struct ubus_event_handler *ev; + + ev = container_of(obj, struct ubus_event_handler, obj); + ev->cb(ctx, ev, method, msg); + return 0; +} + +static const struct ubus_method event_method = { + .name = NULL, + .handler = ubus_event_cb, +}; + +int ubus_register_event_handler(struct ubus_context *ctx, + struct ubus_event_handler *ev, + const char *pattern) +{ + struct ubus_object *obj = &ev->obj; + struct blob_buf b2; + int ret; + + if (!obj->id) { + obj->methods = &event_method; + obj->n_methods = 1; + + if (!!obj->name ^ !!obj->type) + return UBUS_STATUS_INVALID_ARGUMENT; + + ret = ubus_add_object(ctx, obj); + if (ret) + return ret; + } + + /* use a second buffer, ubus_invoke() overwrites the primary one */ + memset(&b2, 0, sizeof(b2)); + blob_buf_init(&b2, 0); + blobmsg_add_u32(&b2, "object", obj->id); + if (pattern) + blobmsg_add_string(&b2, "pattern", pattern); + + ret = ubus_invoke(ctx, UBUS_SYSTEM_OBJECT_EVENT, "register", b2.head, + NULL, NULL, 0); + blob_buf_free(&b2); + + return ret; +} + +int ubus_send_event(struct ubus_context *ctx, const char *id, + struct blob_attr *data) +{ + struct ubus_request req; + void *s; + + blob_buf_init(&b, 0); + blob_put_int32(&b, UBUS_ATTR_OBJID, UBUS_SYSTEM_OBJECT_EVENT); + blob_put_string(&b, UBUS_ATTR_METHOD, "send"); + s = blob_nest_start(&b, UBUS_ATTR_DATA); + blobmsg_add_string(&b, "id", id); + blobmsg_add_field(&b, BLOBMSG_TYPE_TABLE, "data", blob_data(data), blob_len(data)); + blob_nest_end(&b, s); + + if (ubus_start_request(ctx, &req, b.head, UBUS_MSG_INVOKE, UBUS_SYSTEM_OBJECT_EVENT) < 0) + return UBUS_STATUS_INVALID_ARGUMENT; + + return ubus_complete_request(ctx, &req, 0); +} + +static void ubus_default_connection_lost(struct ubus_context *ctx) +{ + if (ctx->sock.registered) + uloop_end(); +} + +int ubus_connect_ctx(struct ubus_context *ctx, const char *path) +{ + memset(ctx, 0, sizeof(*ctx)); + + ctx->sock.fd = -1; + ctx->sock.cb = ubus_handle_data; + ctx->connection_lost = ubus_default_connection_lost; + ctx->pending_timer.cb = ubus_process_pending_msg; + + ctx->msgbuf.data = calloc(UBUS_MSG_CHUNK_SIZE, sizeof(char)); + if (!ctx->msgbuf.data) + return -1; + ctx->msgbuf_data_len = UBUS_MSG_CHUNK_SIZE; + + INIT_LIST_HEAD(&ctx->requests); + INIT_LIST_HEAD(&ctx->pending); + avl_init(&ctx->objects, ubus_cmp_id, false, NULL); + if (ubus_reconnect(ctx, path)) { + free(ctx->msgbuf.data); + ctx->msgbuf.data = NULL; + return -1; + } + + return 0; +} + +static void ubus_auto_reconnect_cb(struct uloop_timeout *timeout) +{ + struct ubus_auto_conn *conn = container_of(timeout, struct ubus_auto_conn, timer); + + if (!ubus_reconnect(&conn->ctx, conn->path)) + ubus_add_uloop(&conn->ctx); + else + uloop_timeout_set(timeout, 1000); +} + +static void ubus_auto_disconnect_cb(struct ubus_context *ctx) +{ + struct ubus_auto_conn *conn = container_of(ctx, struct ubus_auto_conn, ctx); + + conn->timer.cb = ubus_auto_reconnect_cb; + uloop_timeout_set(&conn->timer, 1000); +} + +static void ubus_auto_connect_cb(struct uloop_timeout *timeout) +{ + struct ubus_auto_conn *conn = container_of(timeout, struct ubus_auto_conn, timer); + + if (ubus_connect_ctx(&conn->ctx, conn->path)) { + uloop_timeout_set(timeout, 1000); + fprintf(stderr, "failed to connect to ubus\n"); + return; + } + conn->ctx.connection_lost = ubus_auto_disconnect_cb; + if (conn->cb) + conn->cb(&conn->ctx); + ubus_add_uloop(&conn->ctx); +} + +void ubus_auto_connect(struct ubus_auto_conn *conn) +{ + conn->timer.cb = ubus_auto_connect_cb; + ubus_auto_connect_cb(&conn->timer); +} + +struct ubus_context *ubus_connect(const char *path) +{ + struct ubus_context *ctx; + + ctx = calloc(1, sizeof(*ctx)); + if (!ctx) + return NULL; + + if (ubus_connect_ctx(ctx, path)) { + free(ctx); + ctx = NULL; + } + + return ctx; +} + +void ubus_shutdown(struct ubus_context *ctx) +{ + blob_buf_free(&b); + if (!ctx) + return; + close(ctx->sock.fd); + free(ctx->msgbuf.data); +} + +void ubus_free(struct ubus_context *ctx) +{ + ubus_shutdown(ctx); + free(ctx); +} diff --git a/src/3P/ubus/libubus.h b/src/3P/ubus/libubus.h new file mode 100644 index 00000000..8b0be968 --- /dev/null +++ b/src/3P/ubus/libubus.h @@ -0,0 +1,390 @@ +/* + * Copyright (C) 2011-2014 Felix Fietkau + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __LIBUBUS_H +#define __LIBUBUS_H + +#include +#include +#include +#include +#include +#include "ubusmsg.h" +#include "ubus_common.h" + +#define UBUS_MAX_NOTIFY_PEERS 16 + +struct ubus_context; +struct ubus_msg_src; +struct ubus_object; +struct ubus_request; +struct ubus_request_data; +struct ubus_object_data; +struct ubus_event_handler; +struct ubus_subscriber; +struct ubus_notify_request; + +struct ubus_msghdr_buf { + struct ubus_msghdr hdr; + struct blob_attr *data; +}; + +typedef void (*ubus_lookup_handler_t)(struct ubus_context *ctx, + struct ubus_object_data *obj, + void *priv); +typedef int (*ubus_handler_t)(struct ubus_context *ctx, struct ubus_object *obj, + struct ubus_request_data *req, + const char *method, struct blob_attr *msg); +typedef void (*ubus_state_handler_t)(struct ubus_context *ctx, struct ubus_object *obj); +typedef void (*ubus_remove_handler_t)(struct ubus_context *ctx, + struct ubus_subscriber *obj, uint32_t id); +typedef void (*ubus_event_handler_t)(struct ubus_context *ctx, struct ubus_event_handler *ev, + const char *type, struct blob_attr *msg); +typedef void (*ubus_data_handler_t)(struct ubus_request *req, + int type, struct blob_attr *msg); +typedef void (*ubus_fd_handler_t)(struct ubus_request *req, int fd); +typedef void (*ubus_complete_handler_t)(struct ubus_request *req, int ret); +typedef void (*ubus_notify_complete_handler_t)(struct ubus_notify_request *req, + int idx, int ret); +typedef void (*ubus_connect_handler_t)(struct ubus_context *ctx); + +#define UBUS_OBJECT_TYPE(_name, _methods) \ + { \ + .name = _name, \ + .id = 0, \ + .n_methods = ARRAY_SIZE(_methods), \ + .methods = _methods \ + } + +#define __UBUS_METHOD_NOARG(_name, _handler, _tags) \ + .name = _name, \ + .handler = _handler, \ + .tags = _tags + +#define __UBUS_METHOD(_name, _handler, _policy, _tags) \ + __UBUS_METHOD_NOARG(_name, _handler, _tags), \ + .policy = _policy, \ + .n_policy = ARRAY_SIZE(_policy) + +#define UBUS_METHOD(_name, _handler, _policy) \ + { __UBUS_METHOD(_name, _handler, _policy, 0) } + +#define UBUS_METHOD_TAG(_name, _handler, _policy, _tags)\ + { __UBUS_METHOD(_name, _handler, _policy, _tags) } + +#define UBUS_METHOD_MASK(_name, _handler, _policy, _mask) \ + { \ + __UBUS_METHOD(_name, _handler, _policy, 0),\ + .mask = _mask \ + } + +#define UBUS_METHOD_NOARG(_name, _handler) \ + { __UBUS_METHOD_NOARG(_name, _handler, 0) } + +#define UBUS_METHOD_TAG_NOARG(_name, _handler, _tags) \ + { __UBUS_METHOD_NOARG(_name, _handler, _tags) } + +#define UBUS_TAG_STATUS BIT(0) +#define UBUS_TAG_ADMIN BIT(1) +#define UBUS_TAG_PRIVATE BIT(2) + +struct ubus_method { + const char *name; + ubus_handler_t handler; + + unsigned long mask; + unsigned long tags; + const struct blobmsg_policy *policy; + int n_policy; +}; + +struct ubus_object_type { + const char *name; + uint32_t id; + + const struct ubus_method *methods; + int n_methods; +}; + +struct ubus_object { + struct avl_node avl; + + const char *name; + uint32_t id; + + const char *path; + struct ubus_object_type *type; + + ubus_state_handler_t subscribe_cb; + bool has_subscribers; + + const struct ubus_method *methods; + int n_methods; +}; + +struct ubus_subscriber { + struct ubus_object obj; + + ubus_handler_t cb; + ubus_remove_handler_t remove_cb; +}; + +struct ubus_event_handler { + struct ubus_object obj; + + ubus_event_handler_t cb; + void *priv; // Awox Addon +}; + +struct ubus_context { + struct list_head requests; + struct avl_tree objects; + struct list_head pending; + + struct uloop_fd sock; + struct uloop_timeout pending_timer; + + uint32_t local_id; + uint16_t request_seq; + int stack_depth; + + void (*connection_lost)(struct ubus_context *ctx); + void (*monitor_cb)(struct ubus_context *ctx, uint32_t seq, struct blob_attr *data); + + struct ubus_msghdr_buf msgbuf; + uint32_t msgbuf_data_len; + int msgbuf_reduction_counter; +}; + +struct ubus_object_data { + uint32_t id; + uint32_t type_id; + const char *path; + struct blob_attr *signature; +}; + +struct ubus_acl_key { + const char *user; + const char *group; + const char *object; +}; + +struct ubus_request_data { + uint32_t object; + uint32_t peer; + uint16_t seq; + + struct ubus_acl_key acl; + + /* internal use */ + bool deferred; + int fd; +}; + +struct ubus_request { + struct list_head list; + + struct list_head pending; + int status_code; + bool status_msg; + bool blocked; + bool cancelled; + bool notify; + + uint32_t peer; + uint16_t seq; + + ubus_data_handler_t raw_data_cb; + ubus_data_handler_t data_cb; + ubus_fd_handler_t fd_cb; + ubus_complete_handler_t complete_cb; + + struct ubus_context *ctx; + void *priv; +}; + +struct ubus_notify_request { + struct ubus_request req; + + ubus_notify_complete_handler_t status_cb; + ubus_notify_complete_handler_t complete_cb; + + uint32_t pending; + uint32_t id[UBUS_MAX_NOTIFY_PEERS + 1]; +}; + +struct ubus_auto_conn { + struct ubus_context ctx; + struct uloop_timeout timer; + const char *path; + ubus_connect_handler_t cb; +}; + +struct ubus_context *ubus_connect(const char *path); +int ubus_connect_ctx(struct ubus_context *ctx, const char *path); +void ubus_auto_connect(struct ubus_auto_conn *conn); +int ubus_reconnect(struct ubus_context *ctx, const char *path); + +/* call this only for struct ubus_context pointers returned by ubus_connect() */ +void ubus_free(struct ubus_context *ctx); + +/* call this only for struct ubus_context pointers initialised by ubus_connect_ctx() */ +void ubus_shutdown(struct ubus_context *ctx); + +static inline void ubus_auto_shutdown(struct ubus_auto_conn *conn) +{ + uloop_timeout_cancel(&conn->timer); + ubus_shutdown(&conn->ctx); +} + +const char *ubus_strerror(int error); + +static inline void ubus_add_uloop(struct ubus_context *ctx) +{ + uloop_fd_add(&ctx->sock, ULOOP_BLOCKING | ULOOP_READ); +} + +/* call this for read events on ctx->sock.fd when not using uloop */ +static inline void ubus_handle_event(struct ubus_context *ctx) +{ + ctx->sock.cb(&ctx->sock, ULOOP_READ); +} + +/* ----------- raw request handling ----------- */ + +/* wait for a request to complete and return its status */ +int ubus_complete_request(struct ubus_context *ctx, struct ubus_request *req, + int timeout); + +/* 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); + +/* ----------- objects ----------- */ + +int ubus_lookup(struct ubus_context *ctx, const char *path, + ubus_lookup_handler_t cb, void *priv); + +int ubus_lookup_id(struct ubus_context *ctx, const char *path, uint32_t *id); + +/* make an object visible to remote connections */ +int ubus_add_object(struct ubus_context *ctx, struct ubus_object *obj); + +/* remove the object from the ubus connection */ +int ubus_remove_object(struct ubus_context *ctx, struct ubus_object *obj); + +/* add a subscriber notifications from another object */ +int ubus_register_subscriber(struct ubus_context *ctx, struct ubus_subscriber *obj); + +static inline int +ubus_unregister_subscriber(struct ubus_context *ctx, struct ubus_subscriber *obj) +{ + return ubus_remove_object(ctx, &obj->obj); +} + +int ubus_subscribe(struct ubus_context *ctx, struct ubus_subscriber *obj, uint32_t id); +int ubus_unsubscribe(struct ubus_context *ctx, struct ubus_subscriber *obj, uint32_t id); + +int __ubus_monitor(struct ubus_context *ctx, const char *type); + +static inline int ubus_monitor_start(struct ubus_context *ctx) +{ + return __ubus_monitor(ctx, "add"); +} + +static inline int ubus_monitor_stop(struct ubus_context *ctx) +{ + return __ubus_monitor(ctx, "remove"); +} + + +/* ----------- acl ----------- */ + +struct acl_object { + struct ubus_acl_key key; + struct avl_node avl; + struct blob_attr *acl; +}; + +extern struct avl_tree acl_objects; +int ubus_register_acl(struct ubus_context *ctx); + +#define acl_for_each(o, m) \ + if ((m)->object && (m)->user && (m)->group) \ + avl_for_element_range(avl_find_ge_element(&acl_objects, m, o, avl), avl_find_le_element(&acl_objects, m, o, avl), o, avl) + +/* ----------- 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, + int timeout); + +/* asynchronous version of ubus_invoke() */ +int ubus_invoke_async(struct ubus_context *ctx, uint32_t obj, const char *method, + struct blob_attr *msg, struct ubus_request *req); + +/* send a reply to an incoming object method call */ +int ubus_send_reply(struct ubus_context *ctx, struct ubus_request_data *req, + struct blob_attr *msg); + +static inline void ubus_defer_request(struct ubus_context *ctx, + struct ubus_request_data *req, + struct ubus_request_data *new_req) +{ + (void) ctx; + memcpy(new_req, req, sizeof(*req)); + req->deferred = true; +} + +static inline void ubus_request_set_fd(struct ubus_context *ctx, + struct ubus_request_data *req, int fd) +{ + (void) ctx; + req->fd = fd; +} + +void ubus_complete_deferred_request(struct ubus_context *ctx, + struct ubus_request_data *req, int ret); + +/* + * send a notification to all subscribers of an object + * if timeout < 0, no reply is expected from subscribers + */ +int ubus_notify(struct ubus_context *ctx, struct ubus_object *obj, + const char *type, struct blob_attr *msg, int timeout); + +int ubus_notify_async(struct ubus_context *ctx, struct ubus_object *obj, + const char *type, struct blob_attr *msg, + struct ubus_notify_request *req); + + +/* ----------- events ----------- */ + +int ubus_send_event(struct ubus_context *ctx, const char *id, + struct blob_attr *data); + +int ubus_register_event_handler(struct ubus_context *ctx, + struct ubus_event_handler *ev, + const char *pattern); + +static inline int ubus_unregister_event_handler(struct ubus_context *ctx, + struct ubus_event_handler *ev) +{ + return ubus_remove_object(ctx, &ev->obj); +} + +#endif diff --git a/src/3P/ubus/libubus/builders/cmake/CMakeLists.txt b/src/3P/ubus/libubus/builders/cmake/CMakeLists.txt new file mode 100644 index 00000000..c504a7c7 --- /dev/null +++ b/src/3P/ubus/libubus/builders/cmake/CMakeLists.txt @@ -0,0 +1,43 @@ +cmake_minimum_required (VERSION 3.0) + +project (libubus) + +set (CMAKE_MODULE_PATH "${MODULE_PATH}") + +set(DISABLE_TARGET_OPTIMIZATION ON) + +include (aw) + +include_directories ($ENV{AWOXCVS}/AwoxAudio/Libs/External/ubus/) + +ADD_DEFINITIONS (-Werror --std=gnu99 -Wmissing-declarations) + +ADD_DEFINITIONS (-DUBUS_MAX_MSGLEN=1048576) +ADD_DEFINITIONS (-DUBUS_UNIX_SOCKET="/tmp/ubus.sock") + +file ( + GLOB_RECURSE + source_files + + $ENV{AWOXCVS}/AwoxAudio/Libs/External/ubus/libubus.c + $ENV{AWOXCVS}/AwoxAudio/Libs/External/ubus/libubus-io.c + $ENV{AWOXCVS}/AwoxAudio/Libs/External/ubus/libubus-obj.c + $ENV{AWOXCVS}/AwoxAudio/Libs/External/ubus/libubus-sub.c + $ENV{AWOXCVS}/AwoxAudio/Libs/External/ubus/libubus-req.c + $ENV{AWOXCVS}/AwoxAudio/Libs/External/ubus/libubus-acl.c + ) + +# Library +add_library (ubus SHARED ${source_files}) +target_link_libraries (ubus + LINK_PUBLIC + ubox) + +target_include_directories (ubus PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) + +install (TARGETS ubus + LIBRARY DESTINATION local/lib +) + +file (GLOB ubus_headers $ENV{AWOXCVS}/AwoxAudio/Libs/External/ubus/*.h) +install (FILES ${ubus_headers} DESTINATION include/ubus) diff --git a/src/3P/ubus/lua/CMakeLists.txt b/src/3P/ubus/lua/CMakeLists.txt new file mode 100644 index 00000000..e4821bf7 --- /dev/null +++ b/src/3P/ubus/lua/CMakeLists.txt @@ -0,0 +1,52 @@ +cmake_minimum_required(VERSION 2.6) + +PROJECT(ubus C) + +SET(CMAKE_INSTALL_PREFIX /) + +IF(NOT LUA_CFLAGS) + FIND_PROGRAM(PKG_CONFIG pkg-config) + IF(PKG_CONFIG) + EXECUTE_PROCESS( + COMMAND pkg-config --silence-errors --cflags lua5.1 + OUTPUT_VARIABLE LUA_CFLAGS + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + ENDIF() +ENDIF() + +ADD_DEFINITIONS(-Os -Wall -Werror --std=gnu99 -g3 -I.. ${LUA_CFLAGS}) +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/..) +LINK_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/..) + +IF(APPLE) + SET(CMAKE_SHARED_MODULE_CREATE_C_FLAGS "${CMAKE_SHARED_MODULE_CREATE_C_FLAGS} -undefined dynamic_lookup") +ENDIF(APPLE) + +IF(NOT LUAPATH) + EXECUTE_PROCESS( + COMMAND lua -e "for k in string.gmatch(package.cpath .. \";\", \"([^;]+)/..so;\") do if k:sub(1,1) == \"/\" then print(k) break end end" + OUTPUT_VARIABLE LUAPATH + RESULT_VARIABLE LUA_CHECK_RES + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + + IF(BUILD_LUA) + IF(NOT ${LUA_CHECK_RES} EQUAL 0 OR "${LUAPATH}" EQUAL "") + MESSAGE(SEND_ERROR "Lua was not found on your system") + ENDIF() + ENDIF() +ENDIF() + +IF(BUILD_LUA) + ADD_LIBRARY(ubus_lua MODULE ubus.c) + SET_TARGET_PROPERTIES(ubus_lua PROPERTIES + OUTPUT_NAME ubus + PREFIX "" + ) + TARGET_LINK_LIBRARIES(ubus_lua ubus) + + INSTALL(TARGETS ubus_lua + LIBRARY DESTINATION ${LUAPATH} + ) +ENDIF() diff --git a/src/3P/ubus/lua/builders/linux-gcc/.gitignore b/src/3P/ubus/lua/builders/linux-gcc/.gitignore new file mode 100644 index 00000000..04bc145d --- /dev/null +++ b/src/3P/ubus/lua/builders/linux-gcc/.gitignore @@ -0,0 +1 @@ +_* diff --git a/src/3P/ubus/lua/publisher.lua b/src/3P/ubus/lua/publisher.lua new file mode 100644 index 00000000..8ee3b838 --- /dev/null +++ b/src/3P/ubus/lua/publisher.lua @@ -0,0 +1,60 @@ +#!/usr/bin/env lua + +require "ubus" +require "uloop" + +--[[ + A demo of ubus publisher binding. Should be run before subscriber.lua +--]] + + +uloop.init() + +local conn = ubus.connect() +if not conn then + error("Failed to connect to ubus") +end + +local ubus_objects = { + test = { + hello = { + function(req, msg) + conn:reply(req, {message="foo"}); + print("Call to function 'hello'") + for k, v in pairs(msg) do + print("key=" .. k .. " value=" .. tostring(v)) + end + end, {id = ubus.INT32, msg = ubus.STRING } + }, + hello1 = { + function(req) + conn:reply(req, {message="foo1"}); + conn:reply(req, {message="foo2"}); + print("Call to function 'hello1'") + end, {id = ubus.INT32, msg = ubus.STRING } + }, + __subscriber_cb = function( subs ) + print("total subs: ", subs ) + end + } +} + +conn:add( ubus_objects ) +print("Objects added, starting loop") + +-- start time +local timer +local counter = 0 +function t() + counter = counter + 1 + local params = { + count = counter + } + conn:notify( ubus_objects.test.__ubusobj, "test.alarm", params ) + timer:set(10000) +end +timer = uloop.timer(t) +timer:set(1000) + + +uloop.run() diff --git a/src/3P/ubus/lua/subscriber.lua b/src/3P/ubus/lua/subscriber.lua new file mode 100644 index 00000000..e1d3a9f3 --- /dev/null +++ b/src/3P/ubus/lua/subscriber.lua @@ -0,0 +1,25 @@ +#!/usr/bin/env lua + +--[[ + A demo of ubus subscriber binding. Should be run after publisher.lua +--]] + +require "ubus" +require "uloop" + +uloop.init() + +local conn = ubus.connect() +if not conn then + error("Failed to connect to ubus") +end + +local sub = { + notify = function( msg ) + print("Count: ", msg["count"]) + end, +} + +conn:subscribe( "test", sub ) + +uloop.run() diff --git a/src/3P/ubus/lua/test.lua b/src/3P/ubus/lua/test.lua new file mode 100755 index 00000000..d24ac6e4 --- /dev/null +++ b/src/3P/ubus/lua/test.lua @@ -0,0 +1,54 @@ +#!/usr/bin/env lua + +require "ubus" +require "uloop" + +uloop.init() + +local conn = ubus.connect() +if not conn then + error("Failed to connect to ubus") +end + +local my_method = { + broken = { + hello = 1, + hello1 = { + function(req) + end, {id = "fail" } + }, + }, + test = { + hello = { + function(req, msg) + conn:reply(req, {message="foo"}); + print("Call to function 'hello'") + for k, v in pairs(msg) do + print("key=" .. k .. " value=" .. tostring(v)) + end + end, {id = ubus.INT32, msg = ubus.STRING } + }, + hello1 = { + function(req) + conn:reply(req, {message="foo1"}); + conn:reply(req, {message="foo2"}); + print("Call to function 'hello1'") + end, {id = ubus.INT32, msg = ubus.STRING } + } + } +} + +conn:add(my_method) + +local my_event = { + test = function(msg) + print("Call to test event") + for k, v in pairs(msg) do + print("key=" .. k .. " value=" .. tostring(v)) + end + end, +} + +conn:listen(my_event) + +uloop.run() diff --git a/src/3P/ubus/lua/test_client.lua b/src/3P/ubus/lua/test_client.lua new file mode 100755 index 00000000..0b60e0dc --- /dev/null +++ b/src/3P/ubus/lua/test_client.lua @@ -0,0 +1,41 @@ +#!/usr/bin/env lua + +require "ubus" +require "uloop" + +uloop.init() + +local conn = ubus.connect() +if not conn then + error("Failed to connect to ubusd") +end + +local namespaces = conn:objects() +for i, n in ipairs(namespaces) do + print("namespace=" .. n) + local signatures = conn:signatures(n) + for p, s in pairs(signatures) do + print("\tprocedure=" .. p) + for k, v in pairs(s) do + print("\t\tattribute=" .. k .. " type=" .. v) + end + end +end + +local status = conn:call("test", "hello", { msg = "eth0" }) + +for k, v in pairs(status) do + print("key=" .. k .. " value=" .. tostring(v)) +end + +local status = {conn:call("test", "hello1", { msg = "eth0" })} + +for a = 1, #status do + for k, v in pairs(status[a]) do + print("key=" .. k .. " value=" .. tostring(v)) + end +end + +conn:send("test", { foo = "bar"}) + +uloop.run() diff --git a/src/3P/ubus/lua/ubus.c b/src/3P/ubus/lua/ubus.c new file mode 100644 index 00000000..f59af90c --- /dev/null +++ b/src/3P/ubus/lua/ubus.c @@ -0,0 +1,990 @@ +/* + * Copyright (C) 2012 Jo-Philipp Wich + * Copyright (C) 2012 John Crispin + * Copyright (C) 2016 Iain Fraser + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include + +#define MODNAME "ubus" +#define METANAME MODNAME ".meta" + +static lua_State *state; + +struct ubus_lua_connection { + int timeout; + struct blob_buf buf; + struct ubus_context *ctx; +}; + +struct ubus_lua_object { + struct ubus_object o; + int r; + int rsubscriber; +}; + +struct ubus_lua_event { + struct ubus_event_handler e; + int r; +}; + +struct ubus_lua_subscriber { + struct ubus_subscriber s; + int rnotify; + int rremove; +}; + +static int +ubus_lua_parse_blob(lua_State *L, struct blob_attr *attr, bool table); + +static int +ubus_lua_parse_blob_array(lua_State *L, struct blob_attr *attr, int len, bool table) +{ + int rv; + int idx = 1; + int rem = len; + struct blob_attr *pos; + + lua_newtable(L); + + __blob_for_each_attr(pos, attr, rem) + { + rv = ubus_lua_parse_blob(L, pos, table); + + if (rv > 1) + lua_rawset(L, -3); + else if (rv > 0) + lua_rawseti(L, -2, idx++); + } + + return 1; +} + +static int +ubus_lua_parse_blob(lua_State *L, struct blob_attr *attr, bool table) +{ + int len; + int off = 0; + void *data; + + if (!blobmsg_check_attr(attr, false)) + return 0; + + if (table && blobmsg_name(attr)[0]) + { + lua_pushstring(L, blobmsg_name(attr)); + off++; + } + + data = blobmsg_data(attr); + len = blobmsg_data_len(attr); + + switch (blob_id(attr)) + { + case BLOBMSG_TYPE_BOOL: + lua_pushboolean(L, *(uint8_t *)data); + break; + + case BLOBMSG_TYPE_INT16: + lua_pushinteger(L, be16_to_cpu(*(uint16_t *)data)); + break; + + case BLOBMSG_TYPE_INT32: + lua_pushinteger(L, be32_to_cpu(*(uint32_t *)data)); + break; + + case BLOBMSG_TYPE_INT64: + lua_pushnumber(L, (double) be64_to_cpu(*(uint64_t *)data)); + break; + + case BLOBMSG_TYPE_STRING: + lua_pushstring(L, data); + break; + + case BLOBMSG_TYPE_ARRAY: + ubus_lua_parse_blob_array(L, data, len, false); + break; + + case BLOBMSG_TYPE_TABLE: + ubus_lua_parse_blob_array(L, data, len, true); + break; + + default: + lua_pushnil(L); + break; + } + + return off + 1; +} + + +static bool +ubus_lua_format_blob_is_array(lua_State *L) +{ + lua_Integer prv = 0; + lua_Integer cur = 0; + + /* Find out whether table is array-like */ + for (lua_pushnil(L); lua_next(L, -2); lua_pop(L, 1)) + { +#ifdef LUA_TINT + if (lua_type(L, -2) != LUA_TNUMBER && lua_type(L, -2) != LUA_TINT) +#else + if (lua_type(L, -2) != LUA_TNUMBER) +#endif + { + lua_pop(L, 2); + return false; + } + + cur = lua_tointeger(L, -2); + + if ((cur - 1) != prv) + { + lua_pop(L, 2); + return false; + } + + prv = cur; + } + + return true; +} + +static int +ubus_lua_format_blob_array(lua_State *L, struct blob_buf *b, bool table); + +static int +ubus_lua_format_blob(lua_State *L, struct blob_buf *b, bool table) +{ + void *c; + bool rv = true; + const char *key = table ? lua_tostring(L, -2) : NULL; + + switch (lua_type(L, -1)) + { + case LUA_TBOOLEAN: + blobmsg_add_u8(b, key, (uint8_t)lua_toboolean(L, -1)); + break; + +#ifdef LUA_TINT + case LUA_TINT: +#endif + case LUA_TNUMBER: + blobmsg_add_u32(b, key, (uint32_t)lua_tointeger(L, -1)); + break; + + case LUA_TSTRING: + case LUA_TUSERDATA: + case LUA_TLIGHTUSERDATA: + blobmsg_add_string(b, key, lua_tostring(L, -1)); + break; + + case LUA_TTABLE: + if (ubus_lua_format_blob_is_array(L)) + { + c = blobmsg_open_array(b, key); + rv = ubus_lua_format_blob_array(L, b, false); + blobmsg_close_array(b, c); + } + else + { + c = blobmsg_open_table(b, key); + rv = ubus_lua_format_blob_array(L, b, true); + blobmsg_close_table(b, c); + } + break; + + default: + rv = false; + break; + } + + return rv; +} + +static int +ubus_lua_format_blob_array(lua_State *L, struct blob_buf *b, bool table) +{ + for (lua_pushnil(L); lua_next(L, -2); lua_pop(L, 1)) + { + if (!ubus_lua_format_blob(L, b, table)) + { + lua_pop(L, 1); + return false; + } + } + + return true; +} + + +static int +ubus_lua_connect(lua_State *L) +{ + struct ubus_lua_connection *c; + const char *sockpath = luaL_optstring(L, 1, NULL); + int timeout = luaL_optint(L, 2, 30); + + if ((c = lua_newuserdata(L, sizeof(*c))) != NULL && + (c->ctx = ubus_connect(sockpath)) != NULL) + { + ubus_add_uloop(c->ctx); + c->timeout = timeout; + memset(&c->buf, 0, sizeof(c->buf)); + luaL_getmetatable(L, METANAME); + lua_setmetatable(L, -2); + return 1; + } + + /* NB: no errors from ubus_connect() yet */ + lua_pushnil(L); + lua_pushinteger(L, UBUS_STATUS_UNKNOWN_ERROR); + return 2; +} + + +static void +ubus_lua_objects_cb(struct ubus_context *c, struct ubus_object_data *o, void *p) +{ + lua_State *L = (lua_State *)p; + + lua_pushstring(L, o->path); + lua_rawseti(L, -2, lua_objlen(L, -2) + 1); +} + +static int +ubus_lua_objects(lua_State *L) +{ + int rv; + struct ubus_lua_connection *c = luaL_checkudata(L, 1, METANAME); + + lua_newtable(L); + rv = ubus_lookup(c->ctx, NULL, ubus_lua_objects_cb, L); + + if (rv != UBUS_STATUS_OK) + { + lua_pop(L, 1); + lua_pushnil(L); + lua_pushinteger(L, rv); + return 2; + } + + return 1; +} + +static int +ubus_method_handler(struct ubus_context *ctx, struct ubus_object *obj, + struct ubus_request_data *req, const char *method, + struct blob_attr *msg) +{ + struct ubus_lua_object *o = container_of(obj, struct ubus_lua_object, o); + int rv = 0; + + lua_getglobal(state, "__ubus_cb"); + lua_rawgeti(state, -1, o->r); + lua_getfield(state, -1, method); + lua_remove(state, -2); + lua_remove(state, -2); + + if (lua_isfunction(state, -1)) { + lua_pushlightuserdata(state, req); + if (!msg) + lua_pushnil(state); + else + ubus_lua_parse_blob_array(state, blob_data(msg), blob_len(msg), true); + lua_call(state, 2, 1); + if (lua_isnumber(state, -1)) + rv = lua_tonumber(state, -1); + } + + lua_pop(state, 1); + + return rv; +} + +static int lua_gettablelen(lua_State *L, int index) +{ + int cnt = 0; + + lua_pushnil(L); + index -= 1; + while (lua_next(L, index) != 0) { + cnt++; + lua_pop(L, 1); + } + + return cnt; +} + +static int ubus_lua_reply(lua_State *L) +{ + struct ubus_lua_connection *c = luaL_checkudata(L, 1, METANAME); + struct ubus_request_data *req; + + luaL_checktype(L, 3, LUA_TTABLE); + blob_buf_init(&c->buf, 0); + + if (!ubus_lua_format_blob_array(L, &c->buf, true)) + { + lua_pushnil(L); + lua_pushinteger(L, UBUS_STATUS_INVALID_ARGUMENT); + return 2; + } + + req = lua_touserdata(L, 2); + ubus_send_reply(c->ctx, req, c->buf.head); + + return 0; +} + +static int ubus_lua_load_methods(lua_State *L, struct ubus_method *m) +{ + struct blobmsg_policy *p; + int plen; + int pidx = 0; + + /* get the function pointer */ + lua_pushinteger(L, 1); + lua_gettable(L, -2); + + /* get the policy table */ + lua_pushinteger(L, 2); + lua_gettable(L, -3); + + /* check if the method table is valid */ + if ((lua_type(L, -2) != LUA_TFUNCTION) || + (lua_type(L, -1) != LUA_TTABLE) || + lua_objlen(L, -1)) { + lua_pop(L, 2); + return 1; + } + + /* store function pointer */ + lua_pushvalue(L, -2); + lua_setfield(L, -6, lua_tostring(L, -5)); + + m->name = lua_tostring(L, -4); + m->handler = ubus_method_handler; + + plen = lua_gettablelen(L, -1); + + /* exit if policy table is empty */ + if (!plen) { + lua_pop(L, 2); + return 0; + } + + /* setup the policy pointers */ + p = malloc(sizeof(struct blobmsg_policy) * plen); + if (!p) + return 1; + + memset(p, 0, sizeof(struct blobmsg_policy) * plen); + m->policy = p; + lua_pushnil(L); + while (lua_next(L, -2) != 0) { + int val = lua_tointeger(L, -1); + + /* check if the policy is valid */ + if ((lua_type(L, -2) != LUA_TSTRING) || + (lua_type(L, -1) != LUA_TNUMBER) || + (val < 0) || + (val > BLOBMSG_TYPE_LAST)) { + lua_pop(L, 1); + continue; + } + p[pidx].name = lua_tostring(L, -2); + p[pidx].type = val; + lua_pop(L, 1); + pidx++; + } + + m->n_policy = pidx; + lua_pop(L, 2); + + return 0; +} + +static void +ubus_new_sub_cb(struct ubus_context *ctx, struct ubus_object *obj) +{ + struct ubus_lua_object *luobj; + + luobj = container_of(obj, struct ubus_lua_object, o); + + lua_getglobal(state, "__ubus_cb_publisher"); + lua_rawgeti(state, -1, luobj->rsubscriber); + lua_remove(state, -2); + + if (lua_isfunction(state, -1)) { + lua_pushnumber(state, luobj->o.has_subscribers ); + lua_call(state, 1, 0); + } else { + lua_pop(state, 1); + } +} + +static void +ubus_lua_load_newsub_cb( lua_State *L, struct ubus_lua_object *obj ) +{ + /* keep ref to func */ + lua_getglobal(L, "__ubus_cb_publisher"); + lua_pushvalue(L, -2); + obj->rsubscriber = luaL_ref(L, -2); + lua_pop(L, 1); + + /* real callback */ + obj->o.subscribe_cb = ubus_new_sub_cb; + return; +} + +static struct ubus_object* ubus_lua_load_object(lua_State *L) +{ + struct ubus_lua_object *obj = NULL; + int mlen = lua_gettablelen(L, -1); + struct ubus_method *m; + int midx = 0; + + /* setup object pointers */ + obj = malloc(sizeof(struct ubus_lua_object)); + if (!obj) + return NULL; + + memset(obj, 0, sizeof(struct ubus_lua_object)); + obj->o.name = lua_tostring(L, -2); + + /* setup method pointers */ + m = malloc(sizeof(struct ubus_method) * mlen); + memset(m, 0, sizeof(struct ubus_method) * mlen); + obj->o.methods = m; + + /* setup type pointers */ + obj->o.type = malloc(sizeof(struct ubus_object_type)); + if (!obj->o.type) { + free(obj); + return NULL; + } + + memset(obj->o.type, 0, sizeof(struct ubus_object_type)); + obj->o.type->name = lua_tostring(L, -2); + obj->o.type->id = 0; + obj->o.type->methods = obj->o.methods; + + /* create the callback lookup table */ + lua_createtable(L, 1, 0); + lua_getglobal(L, "__ubus_cb"); + lua_pushvalue(L, -2); + obj->r = luaL_ref(L, -2); + lua_pop(L, 1); + + /* scan each method */ + lua_pushnil(L); + while (lua_next(L, -3) != 0) { + /* check if its the subscriber notification callback */ + if( lua_type( L, -2 ) == LUA_TSTRING && + lua_type( L, -1 ) == LUA_TFUNCTION ){ + if( !strcmp( lua_tostring( L, -2 ), "__subscriber_cb" ) ) + ubus_lua_load_newsub_cb( L, obj ); + } + + /* check if it looks like a method */ + if ((lua_type(L, -2) != LUA_TSTRING) || + (lua_type(L, -1) != LUA_TTABLE) || + !lua_objlen(L, -1)) { + lua_pop(L, 1); + continue; + } + + if (!ubus_lua_load_methods(L, &m[midx])) + midx++; + lua_pop(L, 1); + } + + obj->o.type->n_methods = obj->o.n_methods = midx; + + /* pop the callback table */ + lua_pop(L, 1); + + return &obj->o; +} + +static int ubus_lua_add(lua_State *L) +{ + struct ubus_lua_connection *c = luaL_checkudata(L, 1, METANAME); + + /* verify top level object */ + if (lua_istable(L, 1)) { + lua_pushstring(L, "you need to pass a table"); + lua_error(L); + return 0; + } + + /* scan each object */ + lua_pushnil(L); + while (lua_next(L, -2) != 0) { + struct ubus_object *obj = NULL; + + /* check if the object has a table of methods */ + if ((lua_type(L, -2) == LUA_TSTRING) && (lua_type(L, -1) == LUA_TTABLE)) { + obj = ubus_lua_load_object(L); + + if (obj){ + ubus_add_object(c->ctx, obj); + + /* allow future reference of ubus obj */ + lua_pushstring(state,"__ubusobj"); + lua_pushlightuserdata(state, obj); + lua_settable(state,-3); + } + } + lua_pop(L, 1); + } + + return 0; +} + +static int +ubus_lua_notify( lua_State *L ) +{ + struct ubus_lua_connection *c; + struct ubus_object *obj; + const char* method; + + c = luaL_checkudata(L, 1, METANAME); + method = luaL_checkstring(L, 3); + luaL_checktype(L, 4, LUA_TTABLE); + + if( !lua_islightuserdata( L, 2 ) ){ + lua_pushfstring( L, "Invald 2nd parameter, expected ubus obj ref" ); + lua_error( L ); + } + obj = lua_touserdata( L, 2 ); + + /* create parameters from table */ + blob_buf_init(&c->buf, 0); + if( !ubus_lua_format_blob_array( L, &c->buf, true ) ){ + lua_pushfstring( L, "Invalid 4th parameter, expected table of arguments" ); + lua_error( L ); + } + + ubus_notify( c->ctx, obj, method, c->buf.head, -1 ); + return 0; +} + +static void +ubus_lua_signatures_cb(struct ubus_context *c, struct ubus_object_data *o, void *p) +{ + lua_State *L = (lua_State *)p; + + if (!o->signature) + return; + + ubus_lua_parse_blob_array(L, blob_data(o->signature), blob_len(o->signature), true); +} + +static int +ubus_lua_signatures(lua_State *L) +{ + int rv; + struct ubus_lua_connection *c = luaL_checkudata(L, 1, METANAME); + const char *path = luaL_checkstring(L, 2); + + rv = ubus_lookup(c->ctx, path, ubus_lua_signatures_cb, L); + + if (rv != UBUS_STATUS_OK) + { + lua_pop(L, 1); + lua_pushnil(L); + lua_pushinteger(L, rv); + return 2; + } + + return 1; +} + + +static void +ubus_lua_call_cb(struct ubus_request *req, int type, struct blob_attr *msg) +{ + lua_State *L = (lua_State *)req->priv; + + if (!msg && L) + lua_pushnil(L); + + if (msg && L) + ubus_lua_parse_blob_array(L, blob_data(msg), blob_len(msg), true); +} + +static int +ubus_lua_call(lua_State *L) +{ + int rv, top; + uint32_t id; + struct ubus_lua_connection *c = luaL_checkudata(L, 1, METANAME); + const char *path = luaL_checkstring(L, 2); + const char *func = luaL_checkstring(L, 3); + + luaL_checktype(L, 4, LUA_TTABLE); + blob_buf_init(&c->buf, 0); + + if (!ubus_lua_format_blob_array(L, &c->buf, true)) + { + lua_pushnil(L); + lua_pushinteger(L, UBUS_STATUS_INVALID_ARGUMENT); + return 2; + } + + rv = ubus_lookup_id(c->ctx, path, &id); + + if (rv) + { + lua_pushnil(L); + lua_pushinteger(L, rv); + return 2; + } + + top = lua_gettop(L); + rv = ubus_invoke(c->ctx, id, func, c->buf.head, ubus_lua_call_cb, L, c->timeout * 1000); + + if (rv != UBUS_STATUS_OK) + { + lua_pop(L, 1); + lua_pushnil(L); + lua_pushinteger(L, rv); + return 2; + } + + return lua_gettop(L) - top; +} + +static void +ubus_event_handler(struct ubus_context *ctx, struct ubus_event_handler *ev, + const char *type, struct blob_attr *msg) +{ + struct ubus_lua_event *listener = container_of(ev, struct ubus_lua_event, e); + + lua_getglobal(state, "__ubus_cb_event"); + lua_rawgeti(state, -1, listener->r); + lua_remove(state, -2); + + if (lua_isfunction(state, -1)) { + ubus_lua_parse_blob_array(state, blob_data(msg), blob_len(msg), true); + lua_call(state, 1, 0); + } else { + lua_pop(state, 1); + } +} + +static struct ubus_event_handler* +ubus_lua_load_event(lua_State *L) +{ + struct ubus_lua_event* event = NULL; + + event = malloc(sizeof(struct ubus_lua_event)); + if (!event) + return NULL; + + memset(event, 0, sizeof(struct ubus_lua_event)); + event->e.cb = ubus_event_handler; + + /* update the he callback lookup table */ + lua_getglobal(L, "__ubus_cb_event"); + lua_pushvalue(L, -2); + event->r = luaL_ref(L, -2); + lua_setfield(L, -1, lua_tostring(L, -3)); + + return &event->e; +} + +static int +ubus_lua_listen(lua_State *L) { + struct ubus_lua_connection *c = luaL_checkudata(L, 1, METANAME); + + /* verify top level object */ + luaL_checktype(L, 2, LUA_TTABLE); + + /* scan each object */ + lua_pushnil(L); + while (lua_next(L, -2) != 0) { + struct ubus_event_handler *listener; + + /* check if the key is a string and the value is a method */ + if ((lua_type(L, -2) == LUA_TSTRING) && (lua_type(L, -1) == LUA_TFUNCTION)) { + listener = ubus_lua_load_event(L); + if(listener != NULL) { + ubus_register_event_handler(c->ctx, listener, lua_tostring(L, -2)); + } + } + lua_pop(L, 1); + } + return 0; +} + +static void +ubus_sub_remove_handler(struct ubus_context *ctx, struct ubus_subscriber *s, + uint32_t id) +{ + struct ubus_lua_subscriber *sub; + + sub = container_of(s, struct ubus_lua_subscriber, s); + + lua_getglobal(state, "__ubus_cb_subscribe"); + lua_rawgeti(state, -1, sub->rremove); + lua_remove(state, -2); + + if (lua_isfunction(state, -1)) { + lua_call(state, 0, 0); + } else { + lua_pop(state, 1); + } +} + +static int +ubus_sub_notify_handler(struct ubus_context *ctx, struct ubus_object *obj, + struct ubus_request_data *req, const char *method, + struct blob_attr *msg) +{ + struct ubus_subscriber *s; + struct ubus_lua_subscriber *sub; + + s = container_of(obj, struct ubus_subscriber, obj); + sub = container_of(s, struct ubus_lua_subscriber, s); + + lua_getglobal(state, "__ubus_cb_subscribe"); + lua_rawgeti(state, -1, sub->rnotify); + lua_remove(state, -2); + + if (lua_isfunction(state, -1)) { + if( msg ){ + ubus_lua_parse_blob_array(state, blob_data(msg), blob_len(msg), true); + lua_call(state, 1, 0); + } else { + lua_call(state, 0, 0); + } + } else { + lua_pop(state, 1); + } + + return 0; +} + + + +static void +ubus_lua_do_subscribe( struct ubus_context *ctx, lua_State *L, const char* target, + int idxnotify, int idxremove ) +{ + uint32_t id; + int status; + struct ubus_lua_subscriber *sub; + + if( ( status = ubus_lookup_id( ctx, target, &id ) ) ){ + lua_pushfstring( L, "Unable find target, status=%d", status ); + lua_error( L ); + } + + sub = malloc( sizeof( struct ubus_lua_subscriber ) ); + memset( sub, 0, sizeof( struct ubus_lua_subscriber ) ); + if( !sub ){ + lua_pushstring( L, "Out of memory" ); + lua_error( L ); + } + + if( idxnotify ){ + lua_getglobal(L, "__ubus_cb_subscribe"); + lua_pushvalue(L, idxnotify); + sub->rnotify = luaL_ref(L, -2); + lua_pop(L, 1); + sub->s.cb = ubus_sub_notify_handler; + } + + if( idxremove ){ + lua_getglobal(L, "__ubus_cb_subscribe"); + lua_pushvalue(L, idxnotify); + sub->rnotify = luaL_ref(L, -2); + lua_pop(L, 1); + sub->s.remove_cb = ubus_sub_remove_handler; + } + + if( ( status = ubus_register_subscriber( ctx, &sub->s ) ) ){ + lua_pushfstring( L, "Failed to register subscriber, status=%d", status ); + lua_error( L ); + } + + if( ( status = ubus_subscribe( ctx, &sub->s, id) ) ){ + lua_pushfstring( L, "Failed to register subscriber, status=%d", status ); + lua_error( L ); + } +} + +static int +ubus_lua_subscribe(lua_State *L) { + int idxnotify, idxremove, stackstart; + struct ubus_lua_connection *c; + const char* target; + + idxnotify = idxremove = 0; + stackstart = lua_gettop( L ); + + + c = luaL_checkudata(L, 1, METANAME); + target = luaL_checkstring(L, 2); + luaL_checktype(L, 3, LUA_TTABLE); + + + lua_pushstring( L, "notify"); + lua_gettable( L, 3 ); + if( lua_type( L, -1 ) == LUA_TFUNCTION ){ + idxnotify = lua_gettop( L ); + } else { + lua_pop( L, 1 ); + } + + lua_pushstring( L, "remove"); + lua_gettable( L, 3 ); + if( lua_type( L, -1 ) == LUA_TFUNCTION ){ + idxremove = lua_gettop( L ); + } else { + lua_pop( L, 1 ); + } + + if( idxnotify ) + ubus_lua_do_subscribe( c->ctx, L, target, idxnotify, idxremove ); + + if( lua_gettop( L ) > stackstart ) + lua_pop( L, lua_gettop( L ) - stackstart ); + + return 0; +} + +static int +ubus_lua_send(lua_State *L) +{ + struct ubus_lua_connection *c = luaL_checkudata(L, 1, METANAME); + const char *event = luaL_checkstring(L, 2); + + if (*event == 0) + return luaL_argerror(L, 2, "no event name"); + + // Event content convert to ubus form + luaL_checktype(L, 3, LUA_TTABLE); + blob_buf_init(&c->buf, 0); + + if (!ubus_lua_format_blob_array(L, &c->buf, true)) { + lua_pushnil(L); + lua_pushinteger(L, UBUS_STATUS_INVALID_ARGUMENT); + return 2; + } + + // Send the event + ubus_send_event(c->ctx, event, c->buf.head); + + return 0; +} + + + +static int +ubus_lua__gc(lua_State *L) +{ + struct ubus_lua_connection *c = luaL_checkudata(L, 1, METANAME); + + blob_buf_free(&c->buf); + if (c->ctx != NULL) + { + ubus_free(c->ctx); + memset(c, 0, sizeof(*c)); + } + + return 0; +} + +static const luaL_Reg ubus[] = { + { "connect", ubus_lua_connect }, + { "objects", ubus_lua_objects }, + { "add", ubus_lua_add }, + { "notify", ubus_lua_notify }, + { "reply", ubus_lua_reply }, + { "signatures", ubus_lua_signatures }, + { "call", ubus_lua_call }, + { "close", ubus_lua__gc }, + { "listen", ubus_lua_listen }, + { "send", ubus_lua_send }, + { "subscribe", ubus_lua_subscribe }, + { "__gc", ubus_lua__gc }, + { NULL, NULL }, +}; + +/* avoid missing prototype warning */ +int luaopen_ubus(lua_State *L); + +int +luaopen_ubus(lua_State *L) +{ + /* create metatable */ + luaL_newmetatable(L, METANAME); + + /* metatable.__index = metatable */ + lua_pushvalue(L, -1); + lua_setfield(L, -2, "__index"); + + /* fill metatable */ + luaL_register(L, NULL, ubus); + lua_pop(L, 1); + + /* create module */ + luaL_register(L, MODNAME, ubus); + + /* set some enum defines */ + lua_pushinteger(L, BLOBMSG_TYPE_ARRAY); + lua_setfield(L, -2, "ARRAY"); + lua_pushinteger(L, BLOBMSG_TYPE_TABLE); + lua_setfield(L, -2, "TABLE"); + lua_pushinteger(L, BLOBMSG_TYPE_STRING); + lua_setfield(L, -2, "STRING"); + lua_pushinteger(L, BLOBMSG_TYPE_INT64); + lua_setfield(L, -2, "INT64"); + lua_pushinteger(L, BLOBMSG_TYPE_INT32); + lua_setfield(L, -2, "INT32"); + lua_pushinteger(L, BLOBMSG_TYPE_INT16); + lua_setfield(L, -2, "INT16"); + lua_pushinteger(L, BLOBMSG_TYPE_INT8); + lua_setfield(L, -2, "INT8"); + lua_pushinteger(L, BLOBMSG_TYPE_BOOL); + lua_setfield(L, -2, "BOOLEAN"); + + /* used in our callbacks */ + state = L; + + /* create the callback table */ + lua_createtable(L, 1, 0); + lua_setglobal(L, "__ubus_cb"); + + /* create the event table */ + lua_createtable(L, 1, 0); + lua_setglobal(L, "__ubus_cb_event"); + + /* create the subscriber table */ + lua_createtable(L, 1, 0); + lua_setglobal(L, "__ubus_cb_subscribe"); + + /* create the publisher table - notifications of new subs */ + lua_createtable(L, 1, 0); + lua_setglobal(L, "__ubus_cb_publisher"); + return 0; +} diff --git a/src/3P/ubus/ubus_common.h b/src/3P/ubus/ubus_common.h new file mode 100644 index 00000000..4bb99279 --- /dev/null +++ b/src/3P/ubus/ubus_common.h @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2011 Felix Fietkau + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __UBUS_COMMON_H +#define __UBUS_COMMON_H + +#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 + +#endif diff --git a/src/3P/ubus/ubusd.c b/src/3P/ubus/ubusd.c new file mode 100644 index 00000000..7279a706 --- /dev/null +++ b/src/3P/ubus/ubusd.c @@ -0,0 +1,422 @@ +/* + * Copyright (C) 2011-2014 Felix Fietkau + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#ifdef FreeBSD +#include +#endif +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "ubusd.h" + +static struct ubus_msg_buf *ubus_msg_ref(struct ubus_msg_buf *ub) +{ + if (ub->refcount == ~0) + return ubus_msg_new(ub->data, ub->len, false); + + 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; + + ub->fd = -1; + + 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: + if (ub->fd >= 0) + close(ub->fd); + + free(ub); + break; + default: + ub->refcount--; + break; + } +} + +static int ubus_msg_writev(int fd, struct ubus_msg_buf *ub, int offset) +{ + static struct iovec iov[2]; + static struct { + struct cmsghdr h; + int fd; + } fd_buf = { + .h = { + .cmsg_len = sizeof(fd_buf), + .cmsg_level = SOL_SOCKET, + .cmsg_type = SCM_RIGHTS, + }, + }; + struct msghdr msghdr = { + .msg_iov = iov, + .msg_iovlen = ARRAY_SIZE(iov), + .msg_control = &fd_buf, + .msg_controllen = sizeof(fd_buf), + }; + + fd_buf.fd = ub->fd; + if (ub->fd < 0) { + msghdr.msg_control = NULL; + msghdr.msg_controllen = 0; + } + + if (offset < sizeof(ub->hdr)) { + struct ubus_msghdr hdr; + + hdr.version = ub->hdr.version; + hdr.type = ub->hdr.type; + hdr.seq = cpu_to_be16(ub->hdr.seq); + hdr.peer = cpu_to_be32(ub->hdr.peer); + + iov[0].iov_base = ((char *) &hdr) + offset; + iov[0].iov_len = sizeof(hdr) - offset; + iov[1].iov_base = (char *) ub->data; + iov[1].iov_len = ub->len; + + return sendmsg(fd, &msghdr, 0); + } else { + offset -= sizeof(ub->hdr); + return write(fd, ((char *) ub->data) + offset, ub->len - offset); + } +} + +static void ubus_msg_enqueue(struct ubus_client *cl, struct ubus_msg_buf *ub) +{ + if (cl->tx_queue[cl->txq_tail]) + return; + + cl->tx_queue[cl->txq_tail] = ubus_msg_ref(ub); + cl->txq_tail = (cl->txq_tail + 1) % ARRAY_SIZE(cl->tx_queue); +} + +/* takes the msgbuf reference */ +void ubus_msg_send(struct ubus_client *cl, struct ubus_msg_buf *ub, bool free) +{ + int written; + + if (ub->hdr.type != UBUS_MSG_MONITOR) + ubusd_monitor_message(cl, ub, true); + + if (!cl->tx_queue[cl->txq_cur]) { + written = ubus_msg_writev(cl->sock.fd, ub, 0); + if (written >= ub->len + sizeof(ub->hdr)) + goto out; + + if (written < 0) + written = 0; + + cl->txq_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); + } + ubus_msg_enqueue(cl, ub); + +out: + if (free) + ubus_msg_free(ub); +} + +static struct ubus_msg_buf *ubus_msg_head(struct ubus_client *cl) +{ + return cl->tx_queue[cl->txq_cur]; +} + +static void ubus_msg_dequeue(struct ubus_client *cl) +{ + struct ubus_msg_buf *ub = ubus_msg_head(cl); + + if (!ub) + return; + + ubus_msg_free(ub); + cl->txq_ofs = 0; + cl->tx_queue[cl->txq_cur] = NULL; + cl->txq_cur = (cl->txq_cur + 1) % ARRAY_SIZE(cl->tx_queue); +} + +static void handle_client_disconnect(struct ubus_client *cl) +{ + while (ubus_msg_head(cl)) + ubus_msg_dequeue(cl); + + ubusd_monitor_disconnect(cl); + ubusd_proto_free_client(cl); + if (cl->pending_msg_fd >= 0) + close(cl->pending_msg_fd); + 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; + static struct iovec iov; + static struct { + struct cmsghdr h; + int fd; + } fd_buf = { + .h = { + .cmsg_type = SCM_RIGHTS, + .cmsg_level = SOL_SOCKET, + .cmsg_len = sizeof(fd_buf), + } + }; + struct msghdr msghdr = { + .msg_iov = &iov, + .msg_iovlen = 1, + }; + + /* first try to tx more pending data */ + while ((ub = ubus_msg_head(cl))) { + int written; + + written = ubus_msg_writev(sock->fd, ub, cl->txq_ofs); + if (written < 0) { + switch(errno) { + case EINTR: + case EAGAIN: + break; + default: + goto disconnect; + } + break; + } + + cl->txq_ofs += written; + if (cl->txq_ofs < ub->len + sizeof(ub->hdr)) + break; + + ubus_msg_dequeue(cl); + } + + /* prevent further ULOOP_WRITE events if we don't have data + * to send anymore */ + if (!ubus_msg_head(cl) && (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; + + fd_buf.fd = -1; + + iov.iov_base = ((char *) &cl->hdrbuf) + offset; + iov.iov_len = sizeof(cl->hdrbuf) - offset; + + if (cl->pending_msg_fd < 0) { + msghdr.msg_control = &fd_buf; + msghdr.msg_controllen = sizeof(fd_buf); + } else { + msghdr.msg_control = NULL; + msghdr.msg_controllen = 0; + } + + bytes = recvmsg(sock->fd, &msghdr, 0); + if (bytes < 0) + goto out; + + if (fd_buf.fd >= 0) + cl->pending_msg_fd = fd_buf.fd; + + cl->pending_msg_offset += bytes; + if (cl->pending_msg_offset < sizeof(cl->hdrbuf)) + goto out; + + if (blob_pad_len(&cl->hdrbuf.data) > 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; + + cl->hdrbuf.hdr.seq = be16_to_cpu(cl->hdrbuf.hdr.seq); + cl->hdrbuf.hdr.peer = be32_to_cpu(cl->hdrbuf.hdr.peer); + + 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 */ + ub->fd = cl->pending_msg_fd; + cl->pending_msg_fd = -1; + cl->pending_msg_offset = 0; + cl->pending_msg = NULL; + ubusd_monitor_message(cl, ub, false); + ubusd_proto_receive_message(cl, ub); + goto retry; + } + +out: + if (!sock->eof || ubus_msg_head(cl)) + return; + +disconnect: + handle_client_disconnect(cl); +} + +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 = ubusd_proto_new_client(client_fd, client_cb); + if (cl) + uloop_fd_add(&cl->sock, ULOOP_READ | ULOOP_EDGE_TRIGGER); + else + close(client_fd); + + 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, +}; + +static int usage(const char *progname) +{ + fprintf(stderr, "Usage: %s []\n" + "Options: \n" + " -A : Set the path to ACL files\n" + " -s : Set the unix domain socket to listen on\n" + "\n", progname); + return 1; +} + +static void sighup_handler(int sig) +{ + ubusd_acl_load(); +} + +int main(int argc, char **argv) +{ + const char *ubus_socket = UBUS_UNIX_SOCKET; + int ret = 0; + int ch; + + signal(SIGPIPE, SIG_IGN); + signal(SIGHUP, sighup_handler); + + openlog("ubusd", LOG_PID, LOG_DAEMON); + uloop_init(); + + while ((ch = getopt(argc, argv, "A:s:")) != -1) { + switch (ch) { + case 's': + ubus_socket = optarg; + break; + case 'A': + ubusd_acl_dir = optarg; + break; + default: + return usage(argv[0]); + } + } + + unlink(ubus_socket); + umask(0111); + server_fd.fd = usock(USOCK_UNIX | USOCK_SERVER | USOCK_NONBLOCK, ubus_socket, NULL); + if (server_fd.fd < 0) { + perror("usock"); + ret = -1; + goto out; + } + uloop_fd_add(&server_fd, ULOOP_READ | ULOOP_EDGE_TRIGGER); + ubusd_acl_load(); + + uloop_run(); + unlink(ubus_socket); + +out: + uloop_done(); + return ret; +} diff --git a/src/3P/ubus/ubusd.h b/src/3P/ubus/ubusd.h new file mode 100644 index 00000000..5031ed45 --- /dev/null +++ b/src/3P/ubus/ubusd.h @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2011-2014 Felix Fietkau + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __UBUSD_H +#define __UBUSD_H + +#include +#include +#include +#include "ubus_common.h" +#include "ubusd_id.h" +#include "ubusd_obj.h" +#include "ubusmsg.h" +#include "ubusd_acl.h" + +#define UBUSD_CLIENT_BACKLOG 32 +#define UBUS_OBJ_HASH_BITS 4 + +extern struct blob_buf b; + +struct ubus_msg_buf { + uint32_t refcount; /* ~0: uses external data buffer */ + struct ubus_msghdr hdr; + struct blob_attr *data; + int fd; + int len; +}; + +struct ubus_client { + struct ubus_id id; + struct uloop_fd sock; + + uid_t uid; + gid_t gid; + char *user; + char *group; + + struct list_head objects; + + struct ubus_msg_buf *tx_queue[UBUSD_CLIENT_BACKLOG]; + unsigned int txq_cur, txq_tail, txq_ofs; + + struct ubus_msg_buf *pending_msg; + int pending_msg_offset; + int pending_msg_fd; + struct { + struct ubus_msghdr hdr; + struct blob_attr data; + } hdrbuf; +}; + +struct ubus_path { + struct list_head list; + const char name[]; +}; + +extern const char *ubusd_acl_dir; + +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, bool free); +void ubus_msg_free(struct ubus_msg_buf *ub); +struct blob_attr **ubus_parse_msg(struct blob_attr *msg); + +struct ubus_client *ubusd_proto_new_client(int fd, uloop_fd_handler cb); +void ubusd_proto_receive_message(struct ubus_client *cl, struct ubus_msg_buf *ub); +void ubusd_proto_free_client(struct ubus_client *cl); +void ubus_proto_send_msg_from_blob(struct ubus_client *cl, struct ubus_msg_buf *ub, + uint8_t type); + +typedef struct ubus_msg_buf *(*event_fill_cb)(void *priv, const char *id); +void ubusd_event_init(void); +void ubusd_event_cleanup_object(struct ubus_object *obj); +void ubusd_send_obj_event(struct ubus_object *obj, bool add); +int ubusd_send_event(struct ubus_client *cl, const char *id, + event_fill_cb fill_cb, void *cb_priv); + +void ubusd_acl_init(void); + +void ubusd_monitor_init(void); +void ubusd_monitor_message(struct ubus_client *cl, struct ubus_msg_buf *ub, bool send); +void ubusd_monitor_disconnect(struct ubus_client *cl); + +#endif diff --git a/src/3P/ubus/ubusd/builders/cmake/CMakeLists.txt b/src/3P/ubus/ubusd/builders/cmake/CMakeLists.txt new file mode 100644 index 00000000..f9d2a5e1 --- /dev/null +++ b/src/3P/ubus/ubusd/builders/cmake/CMakeLists.txt @@ -0,0 +1,38 @@ +cmake_minimum_required(VERSION 3.0) + +project (ubusd) + +set (CMAKE_MODULE_PATH "${MODULE_PATH}") + +set(DISABLE_TARGET_OPTIMIZATION ON) + +include (aw) + +include_directories ($ENV{AWOXCVS}/AwoxAudio/Libs/External/ubus/) + +ADD_DEFINITIONS (-Werror --std=gnu99 -Wmissing-declarations) + +ADD_DEFINITIONS (-DUBUS_MAX_MSGLEN=1048576) +ADD_DEFINITIONS (-DUBUS_UNIX_SOCKET="/tmp/ubus.sock") + +file ( + GLOB_RECURSE + ubusd_source_files + + $ENV{AWOXCVS}/AwoxAudio/Libs/External/ubus/ubusd.c + $ENV{AWOXCVS}/AwoxAudio/Libs/External/ubus/ubusd_id.c + $ENV{AWOXCVS}/AwoxAudio/Libs/External/ubus/ubusd_obj.c + $ENV{AWOXCVS}/AwoxAudio/Libs/External/ubus/ubusd_proto.c + $ENV{AWOXCVS}/AwoxAudio/Libs/External/ubus/ubusd_event.c + $ENV{AWOXCVS}/AwoxAudio/Libs/External/ubus/ubusd_acl.c + $ENV{AWOXCVS}/AwoxAudio/Libs/External/ubus/ubusd_monitor.c + ) + +# Daemon +add_executable (ubusd ${ubusd_source_files}) +target_link_libraries (ubusd ubox blobmsg_json) + + +install (TARGETS ubusd + RUNTIME DESTINATION ../sbin +) diff --git a/src/3P/ubus/ubusd_acl.c b/src/3P/ubus/ubusd_acl.c new file mode 100644 index 00000000..2a121f3d --- /dev/null +++ b/src/3P/ubus/ubusd_acl.c @@ -0,0 +1,495 @@ +/* + * Copyright (C) 2015 John Crispin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +//#define _GNU_SOURCE +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "ubusd.h" + +#ifndef SO_PEERCRED +struct ucred { + int pid; + int uid; + int gid; +}; +#endif + +struct ubusd_acl_obj { + struct avl_node avl; + struct list_head list; + + const char *user; + const char *group; + + struct blob_attr *methods; + struct blob_attr *tags; + struct blob_attr *priv; + bool subscribe; + bool publish; +}; + +struct ubusd_acl_file { + struct vlist_node avl; + + const char *user; + const char *group; + + struct blob_attr *blob; + struct list_head acl; + + int ok; +}; + +const char *ubusd_acl_dir = "/usr/share/acl.d"; +static struct blob_buf bbuf; +static struct avl_tree ubusd_acls; +static int ubusd_acl_seq; +static struct ubus_object *acl_obj; + +static int +ubusd_acl_match_path(const void *k1, const void *k2, void *ptr) +{ + const char *name = k1; + const char *match = k2; + char *wildcard = strstr(match, "\t"); + + if (wildcard) + return strncmp(name, match, wildcard - match); + + return strcmp(name, match); +} + +static int +ubusd_acl_match_cred(struct ubus_client *cl, struct ubusd_acl_obj *obj) +{ + if (obj->user && !strcmp(cl->user, obj->user)) + return 0; + + if (obj->group && !strcmp(cl->group, obj->group)) + return 0; + + return -1; +} + +int +ubusd_acl_check(struct ubus_client *cl, const char *obj, + const char *method, enum ubusd_acl_type type) +{ + struct ubusd_acl_obj *acl; + struct blob_attr *cur; + int rem; + + if (!cl->uid) + return 0; + + acl = avl_find_ge_element(&ubusd_acls, obj, acl, avl); + if (!acl) + return -1; + + avl_for_element_to_last(&ubusd_acls, acl, acl, avl) { + int diff = ubusd_acl_match_path(obj, acl->avl.key, NULL); + + if (diff) + break; + + if (ubusd_acl_match_cred(cl, acl)) + continue; + + switch (type) { + case UBUS_ACL_PUBLISH: + if (acl->publish) + return 0; + break; + + case UBUS_ACL_SUBSCRIBE: + if (acl->subscribe) + return 0; + break; + + case UBUS_ACL_ACCESS: + if (acl->methods) + blobmsg_for_each_attr(cur, acl->methods, rem) + if (blobmsg_type(cur) == BLOBMSG_TYPE_STRING) + if (!ubusd_acl_match_path(method, blobmsg_get_string(cur), NULL)) + return 0; + break; + } + } + + return -1; +} + +int +ubusd_acl_init_client(struct ubus_client *cl, int fd) +{ + struct ucred cred; + struct passwd *pwd; + struct group *group; + +#ifdef SO_PEERCRED + unsigned int len = sizeof(struct ucred); + + if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &cred, &len) == -1) + return -1; +#else + memset(&cred, 0, sizeof(cred)); +#endif + + pwd = getpwuid(cred.uid); + if (!pwd) + return -1; + + group = getgrgid(cred.gid); + if (!group) + return -1; + + cl->uid = cred.uid; + cl->gid = cred.gid; + + cl->group = strdup(group->gr_name); + cl->user = strdup(pwd->pw_name); + + return 0; +} + +void +ubusd_acl_free_client(struct ubus_client *cl) +{ + free(cl->group); + free(cl->user); +} + +static void +ubusd_acl_file_free(struct ubusd_acl_file *file) +{ + struct ubusd_acl_obj *p, *q; + + list_for_each_entry_safe(p, q, &file->acl, list) { + avl_delete(&ubusd_acls, &p->avl); + list_del(&p->list); + free(p); + } + + free(file); +} + +enum { + ACL_ACCESS_METHODS, + ACL_ACCESS_TAGS, + ACL_ACCESS_PRIV, + __ACL_ACCESS_MAX +}; + +static const struct blobmsg_policy acl_obj_policy[__ACL_ACCESS_MAX] = { + [ACL_ACCESS_METHODS] = { .name = "methods", .type = BLOBMSG_TYPE_ARRAY }, + [ACL_ACCESS_TAGS] = { .name = "tags", .type = BLOBMSG_TYPE_ARRAY }, + [ACL_ACCESS_PRIV] = { .name = "acl", .type = BLOBMSG_TYPE_TABLE }, +}; + +static struct ubusd_acl_obj* +ubusd_acl_alloc_obj(struct ubusd_acl_file *file, const char *obj) +{ + struct ubusd_acl_obj *o; + char *k; + + o = calloc_a(sizeof(*o), &k, strlen(obj) + 1); + o->user = file->user; + o->group = file->group; + o->avl.key = k; + strcpy(k, obj); + + while (*k) { + if (*k == '*') + *k = '\t'; + k++; + } + + list_add(&o->list, &file->acl); + avl_insert(&ubusd_acls, &o->avl); + + return o; +} + +static void +ubusd_acl_add_access(struct ubusd_acl_file *file, struct blob_attr *obj) +{ + struct blob_attr *tb[__ACL_ACCESS_MAX]; + struct ubusd_acl_obj *o; + + blobmsg_parse(acl_obj_policy, __ACL_ACCESS_MAX, tb, blobmsg_data(obj), + blobmsg_data_len(obj)); + + if (!tb[ACL_ACCESS_METHODS] && !tb[ACL_ACCESS_TAGS] && !tb[ACL_ACCESS_PRIV]) + return; + + o = ubusd_acl_alloc_obj(file, blobmsg_name(obj)); + + o->methods = tb[ACL_ACCESS_METHODS]; + o->tags = tb[ACL_ACCESS_TAGS]; + o->priv = tb[ACL_ACCESS_PRIV]; + + if (file->user || file->group) + file->ok = 1; +} + +static void +ubusd_acl_add_subscribe(struct ubusd_acl_file *file, const char *obj) +{ + struct ubusd_acl_obj *o = ubusd_acl_alloc_obj(file, obj); + + o->subscribe = true; +} + +static void +ubusd_acl_add_publish(struct ubusd_acl_file *file, const char *obj) +{ + struct ubusd_acl_obj *o = ubusd_acl_alloc_obj(file, obj); + + o->publish = true; +} + +enum { + ACL_USER, + ACL_GROUP, + ACL_ACCESS, + ACL_PUBLISH, + ACL_SUBSCRIBE, + ACL_INHERIT, + __ACL_MAX +}; + +static const struct blobmsg_policy acl_policy[__ACL_MAX] = { + [ACL_USER] = { .name = "user", .type = BLOBMSG_TYPE_STRING }, + [ACL_GROUP] = { .name = "group", .type = BLOBMSG_TYPE_STRING }, + [ACL_ACCESS] = { .name = "access", .type = BLOBMSG_TYPE_TABLE }, + [ACL_PUBLISH] = { .name = "publish", .type = BLOBMSG_TYPE_ARRAY }, + [ACL_SUBSCRIBE] = { .name = "subscribe", .type = BLOBMSG_TYPE_ARRAY }, + [ACL_INHERIT] = { .name = "inherit", .type = BLOBMSG_TYPE_ARRAY }, +}; + +static void +ubusd_acl_file_add(struct ubusd_acl_file *file) +{ + struct blob_attr *tb[__ACL_MAX], *cur; + int rem; + + blobmsg_parse(acl_policy, __ACL_MAX, tb, blob_data(file->blob), + blob_len(file->blob)); + + if (tb[ACL_USER]) + file->user = blobmsg_get_string(tb[ACL_USER]); + else if (tb[ACL_GROUP]) + file->group = blobmsg_get_string(tb[ACL_GROUP]); + else + return; + + if (tb[ACL_ACCESS]) + blobmsg_for_each_attr(cur, tb[ACL_ACCESS], rem) + ubusd_acl_add_access(file, cur); + + if (tb[ACL_SUBSCRIBE]) + blobmsg_for_each_attr(cur, tb[ACL_SUBSCRIBE], rem) + if (blobmsg_type(cur) == BLOBMSG_TYPE_STRING) + ubusd_acl_add_subscribe(file, blobmsg_get_string(cur)); + + if (tb[ACL_PUBLISH]) + blobmsg_for_each_attr(cur, tb[ACL_PUBLISH], rem) + if (blobmsg_type(cur) == BLOBMSG_TYPE_STRING) + ubusd_acl_add_publish(file, blobmsg_get_string(cur)); +} + +static void +ubusd_acl_update_cb(struct vlist_tree *tree, struct vlist_node *node_new, + struct vlist_node *node_old) +{ + struct ubusd_acl_file *file; + + if (node_old) { + file = container_of(node_old, struct ubusd_acl_file, avl); + ubusd_acl_file_free(file); + } + + if (node_new) { + file = container_of(node_new, struct ubusd_acl_file, avl); + ubusd_acl_file_add(file); + } +} + +static struct ubus_msg_buf * +ubusd_create_sequence_event_msg(void *priv, const char *id) +{ + void *s; + + blob_buf_init(&b, 0); + blob_put_int32(&b, UBUS_ATTR_OBJID, 0); + blob_put_string(&b, UBUS_ATTR_METHOD, id); + s = blob_nest_start(&b, UBUS_ATTR_DATA); + blobmsg_add_u32(&b, "sequence", ubusd_acl_seq); + blob_nest_end(&b, s); + + return ubus_msg_new(b.head, blob_raw_len(b.head), true); +} + +static VLIST_TREE(ubusd_acl_files, avl_strcmp, ubusd_acl_update_cb, false, false); + +static int +ubusd_acl_load_file(const char *filename) +{ + struct ubusd_acl_file *file; + void *blob; + + blob_buf_init(&bbuf, 0); + if (!blobmsg_add_json_from_file(&bbuf, filename)) { + syslog(LOG_ERR, "failed to parse %s\n", filename); + return -1; + } + + file = calloc_a(sizeof(*file), &blob, blob_raw_len(bbuf.head)); + if (!file) + return -1; + + file->blob = blob; + + memcpy(blob, bbuf.head, blob_raw_len(bbuf.head)); + INIT_LIST_HEAD(&file->acl); + + vlist_add(&ubusd_acl_files, &file->avl, filename); + syslog(LOG_INFO, "loading %s\n", filename); + + return 0; +} + +void +ubusd_acl_load(void) +{ + struct stat st; + glob_t gl; + int j; + const char *suffix = "/*.json"; + char *path = alloca(strlen(ubusd_acl_dir) + strlen(suffix) + 1); + + sprintf(path, "%s%s", ubusd_acl_dir, suffix); + if (glob(path, GLOB_NOESCAPE | GLOB_MARK, NULL, &gl)) + return; + + vlist_update(&ubusd_acl_files); + for (j = 0; j < gl.gl_pathc; j++) { + if (stat(gl.gl_pathv[j], &st) || !S_ISREG(st.st_mode)) + continue; + + if (st.st_uid || st.st_gid) { + syslog(LOG_ERR, "%s has wrong owner\n", gl.gl_pathv[j]); + continue; + } + if (st.st_mode & (S_IWOTH | S_IWGRP | S_IXOTH)) { + syslog(LOG_ERR, "%s has wrong permissions\n", gl.gl_pathv[j]); + continue; + } + ubusd_acl_load_file(gl.gl_pathv[j]); + } + + globfree(&gl); + vlist_flush(&ubusd_acl_files); + ubusd_acl_seq++; + ubusd_send_event(NULL, "ubus.acl.sequence", ubusd_create_sequence_event_msg, NULL); +} + +static void +ubusd_reply_add(struct ubus_object *obj) +{ + struct ubusd_acl_obj *acl; + + if (!obj->path.key) + return; + + acl = avl_find_ge_element(&ubusd_acls, obj->path.key, acl, avl); + if (!acl) + return; + + avl_for_element_to_last(&ubusd_acls, acl, acl, avl) { + void *c; + + if (!acl->priv) + continue; + + if (!ubusd_acl_match_path(obj->path.key, acl->avl.key, NULL)) + continue; + + c = blobmsg_open_table(&b, NULL); + blobmsg_add_string(&b, "obj", obj->path.key); + if (acl->user) + blobmsg_add_string(&b, "user", acl->user); + if (acl->group) + blobmsg_add_string(&b, "group", acl->group); + + blobmsg_add_field(&b, blobmsg_type(acl->priv), "acl", + blobmsg_data(acl->priv), blobmsg_data_len(acl->priv)); + + blobmsg_close_table(&b, c); + } +} +static int ubusd_reply_query(struct ubus_client *cl, struct ubus_msg_buf *ub, struct blob_attr **attr, struct blob_attr *msg) +{ + struct ubus_object *obj; + void *d, *a; + + if (!attr[UBUS_ATTR_OBJID]) + return UBUS_STATUS_INVALID_ARGUMENT; + + obj = ubusd_find_object(blob_get_u32(attr[UBUS_ATTR_OBJID])); + if (!obj) + return UBUS_STATUS_NOT_FOUND; + + blob_buf_init(&b, 0); + blob_put_int32(&b, UBUS_ATTR_OBJID, obj->id.id); + d = blob_nest_start(&b, UBUS_ATTR_DATA); + + blobmsg_add_u32(&b, "seq", ubusd_acl_seq); + a = blobmsg_open_array(&b, "acl"); + list_for_each_entry(obj, &cl->objects, list) + ubusd_reply_add(obj); + blobmsg_close_table(&b, a); + + blob_nest_end(&b, d); + + ubus_proto_send_msg_from_blob(cl, ub, UBUS_MSG_DATA); + + return 0; +} + +static int ubusd_acl_recv(struct ubus_client *cl, struct ubus_msg_buf *ub, const char *method, struct blob_attr *msg) +{ + if (!strcmp(method, "query")) + return ubusd_reply_query(cl, ub, ubus_parse_msg(ub->data), msg); + + return UBUS_STATUS_INVALID_COMMAND; +} + +void ubusd_acl_init(void) +{ + avl_init(&ubusd_acls, ubusd_acl_match_path, true, NULL); + acl_obj = ubusd_create_object_internal(NULL, UBUS_SYSTEM_OBJECT_ACL); + acl_obj->recv_msg = ubusd_acl_recv; +} diff --git a/src/3P/ubus/ubusd_acl.h b/src/3P/ubus/ubusd_acl.h new file mode 100644 index 00000000..c5dfd8a9 --- /dev/null +++ b/src/3P/ubus/ubusd_acl.h @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2015 John Crispin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __UBUSD_ACL_H +#define __UBUSD_ACL_H + +enum ubusd_acl_type { + UBUS_ACL_PUBLISH, + UBUS_ACL_SUBSCRIBE, + UBUS_ACL_ACCESS, +}; + +int ubusd_acl_check(struct ubus_client *cl, const char *obj, const char *method, enum ubusd_acl_type type); +int ubusd_acl_init_client(struct ubus_client *cl, int fd); +void ubusd_acl_free_client(struct ubus_client *cl); +void ubusd_acl_load(void); + +#endif diff --git a/src/3P/ubus/ubusd_event.c b/src/3P/ubus/ubusd_event.c new file mode 100644 index 00000000..984f341e --- /dev/null +++ b/src/3P/ubus/ubusd_event.c @@ -0,0 +1,271 @@ +/* + * Copyright (C) 2011 Felix Fietkau + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include "ubusd.h" + +static struct avl_tree patterns; +static struct ubus_object *event_obj; +static int event_seq = 0; +static int obj_event_seq = 1; + +struct event_source { + struct list_head list; + struct ubus_object *obj; + struct avl_node avl; + bool partial; +}; + +static void ubusd_delete_event_source(struct event_source *evs) +{ + list_del(&evs->list); + avl_delete(&patterns, &evs->avl); + free(evs); +} + +void ubusd_event_cleanup_object(struct ubus_object *obj) +{ + struct event_source *ev; + + while (!list_empty(&obj->events)) { + ev = list_first_entry(&obj->events, struct event_source, list); + ubusd_delete_event_source(ev); + } +} + +enum { + EVREG_PATTERN, + EVREG_OBJECT, + EVREG_LAST, +}; + +static struct blobmsg_policy evr_policy[] = { + [EVREG_PATTERN] = { .name = "pattern", .type = BLOBMSG_TYPE_STRING }, + [EVREG_OBJECT] = { .name = "object", .type = BLOBMSG_TYPE_INT32 }, +}; + +static int ubusd_alloc_event_pattern(struct ubus_client *cl, struct blob_attr *msg) +{ + struct event_source *ev; + struct ubus_object *obj; + struct blob_attr *attr[EVREG_LAST]; + char *pattern, *name; + uint32_t id; + bool partial = false; + int len; + + blobmsg_parse(evr_policy, EVREG_LAST, attr, blob_data(msg), blob_len(msg)); + if (!attr[EVREG_OBJECT] || !attr[EVREG_PATTERN]) + return UBUS_STATUS_INVALID_ARGUMENT; + + id = blobmsg_get_u32(attr[EVREG_OBJECT]); + if (id < UBUS_SYSTEM_OBJECT_MAX) + return UBUS_STATUS_PERMISSION_DENIED; + + obj = ubusd_find_object(id); + if (!obj) + return UBUS_STATUS_NOT_FOUND; + + if (obj->client != cl) + return UBUS_STATUS_PERMISSION_DENIED; + + pattern = blobmsg_data(attr[EVREG_PATTERN]); + + len = strlen(pattern); + if (pattern[len - 1] == '*') { + partial = true; + pattern[len - 1] = 0; + len--; + } + + ev = calloc(1, sizeof(*ev) + len + 1); + if (!ev) + return UBUS_STATUS_NO_DATA; + + list_add(&ev->list, &obj->events); + ev->obj = obj; + ev->partial = partial; + name = (char *) (ev + 1); + strcpy(name, pattern); + ev->avl.key = name; + avl_insert(&patterns, &ev->avl); + + return 0; +} + +static void ubusd_send_event_msg(struct ubus_msg_buf **ub, struct ubus_client *cl, + struct ubus_object *obj, const char *id, + event_fill_cb fill_cb, void *cb_priv) +{ + uint32_t *objid_ptr; + + /* do not loop back events */ + if (obj->client == cl) + return; + + /* do not send duplicate events */ + if (obj->event_seen == obj_event_seq) + return; + + obj->event_seen = obj_event_seq; + + if (!*ub) { + *ub = fill_cb(cb_priv, id); + (*ub)->hdr.type = UBUS_MSG_INVOKE; + (*ub)->hdr.peer = 0; + } + + objid_ptr = blob_data(blob_data((*ub)->data)); + *objid_ptr = htonl(obj->id.id); + + (*ub)->hdr.seq = ++event_seq; + ubus_msg_send(obj->client, *ub, false); +} + +static bool strmatch_len(const char *s1, const char *s2, int *len) +{ + for (*len = 0; s1[*len] == s2[*len]; (*len)++) + if (!s1[*len]) + return true; + + return false; +} + +int ubusd_send_event(struct ubus_client *cl, const char *id, + event_fill_cb fill_cb, void *cb_priv) +{ + struct ubus_msg_buf *ub = NULL; + struct event_source *ev; + int match_len = 0; + + obj_event_seq++; + + /* + * Since this tree is sorted alphabetically, we can only expect to find + * matching entries as long as the number of matching characters + * between the pattern string and our string is monotonically increasing. + */ + avl_for_each_element(&patterns, ev, avl) { + const char *key = ev->avl.key; + int cur_match_len; + bool full_match; + + full_match = strmatch_len(id, key, &cur_match_len); + if (cur_match_len < match_len) + break; + + match_len = cur_match_len; + + if (!full_match) { + if (!ev->partial) + continue; + + if (match_len != strlen(key)) + continue; + } + + ubusd_send_event_msg(&ub, cl, ev->obj, id, fill_cb, cb_priv); + } + + if (ub) + ubus_msg_free(ub); + + return 0; +} + +enum { + EVMSG_ID, + EVMSG_DATA, + EVMSG_LAST, +}; + +static struct blobmsg_policy ev_policy[] = { + [EVMSG_ID] = { .name = "id", .type = BLOBMSG_TYPE_STRING }, + [EVMSG_DATA] = { .name = "data", .type = BLOBMSG_TYPE_TABLE }, +}; + +static struct ubus_msg_buf * +ubusd_create_event_from_msg(void *priv, const char *id) +{ + struct blob_attr *msg = priv; + + blob_buf_init(&b, 0); + blob_put_int32(&b, UBUS_ATTR_OBJID, 0); + blob_put_string(&b, UBUS_ATTR_METHOD, id); + blob_put(&b, UBUS_ATTR_DATA, blobmsg_data(msg), blobmsg_data_len(msg)); + + return ubus_msg_new(b.head, blob_raw_len(b.head), true); +} + +static int ubusd_forward_event(struct ubus_client *cl, struct blob_attr *msg) +{ + struct blob_attr *data; + struct blob_attr *attr[EVMSG_LAST]; + const char *id; + + blobmsg_parse(ev_policy, EVMSG_LAST, attr, blob_data(msg), blob_len(msg)); + if (!attr[EVMSG_ID] || !attr[EVMSG_DATA]) + return UBUS_STATUS_INVALID_ARGUMENT; + + id = blobmsg_data(attr[EVMSG_ID]); + data = attr[EVMSG_DATA]; + + if (!strncmp(id, "ubus.", 5)) + return UBUS_STATUS_PERMISSION_DENIED; + + return ubusd_send_event(cl, id, ubusd_create_event_from_msg, data); +} + +static int ubusd_event_recv(struct ubus_client *cl, struct ubus_msg_buf *ub, const char *method, struct blob_attr *msg) +{ + if (!strcmp(method, "register")) + return ubusd_alloc_event_pattern(cl, msg); + + if (!strcmp(method, "send")) + return ubusd_forward_event(cl, msg); + + return UBUS_STATUS_INVALID_COMMAND; +} + +static struct ubus_msg_buf * +ubusd_create_object_event_msg(void *priv, const char *id) +{ + struct ubus_object *obj = priv; + void *s; + + blob_buf_init(&b, 0); + blob_put_int32(&b, UBUS_ATTR_OBJID, 0); + blob_put_string(&b, UBUS_ATTR_METHOD, id); + s = blob_nest_start(&b, UBUS_ATTR_DATA); + blobmsg_add_u32(&b, "id", obj->id.id); + blobmsg_add_string(&b, "path", obj->path.key); + blob_nest_end(&b, s); + + return ubus_msg_new(b.head, blob_raw_len(b.head), true); +} + +void ubusd_send_obj_event(struct ubus_object *obj, bool add) +{ + const char *id = add ? "ubus.object.add" : "ubus.object.remove"; + + ubusd_send_event(NULL, id, ubusd_create_object_event_msg, obj); +} + +void ubusd_event_init(void) +{ + ubus_init_string_tree(&patterns, true); + event_obj = ubusd_create_object_internal(NULL, UBUS_SYSTEM_OBJECT_EVENT); + if (event_obj != NULL) + event_obj->recv_msg = ubusd_event_recv; +} + diff --git a/src/3P/ubus/ubusd_id.c b/src/3P/ubus/ubusd_id.c new file mode 100644 index 00000000..8d9fede2 --- /dev/null +++ b/src/3P/ubus/ubusd_id.c @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2011 Felix Fietkau + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include + +#include "ubusmsg.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_string_tree(struct avl_tree *tree, bool dup) +{ + avl_init(tree, avl_strcmp, dup, NULL); +} + +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, uint32_t val) +{ + id->avl.key = &id->id; + if (val) { + id->id = val; + return avl_insert(tree, &id->avl) == 0; + } + + do { + if (read(random_fd, &id->id, sizeof(id->id)) != sizeof(id->id)) + return false; + + if (id->id < UBUS_SYSTEM_OBJECT_MAX) + continue; + } while (avl_insert(tree, &id->avl) != 0); + + return true; +} + diff --git a/src/3P/ubus/ubusd_id.h b/src/3P/ubus/ubusd_id.h new file mode 100644 index 00000000..0c852485 --- /dev/null +++ b/src/3P/ubus/ubusd_id.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2011 Felix Fietkau + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __UBUSD_ID_H +#define __UBUSD_ID_H + +#include +#include + +struct ubus_id { + struct avl_node avl; + uint32_t id; +}; + +void ubus_init_id_tree(struct avl_tree *tree); +void ubus_init_string_tree(struct avl_tree *tree, bool dup); +bool ubus_alloc_id(struct avl_tree *tree, struct ubus_id *id, uint32_t val); + +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/src/3P/ubus/ubusd_monitor.c b/src/3P/ubus/ubusd_monitor.c new file mode 100644 index 00000000..82d03330 --- /dev/null +++ b/src/3P/ubus/ubusd_monitor.c @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2015 Felix Fietkau + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "ubusd.h" + +static struct ubus_object *monitor_obj; +static LIST_HEAD(monitors); + +struct ubus_monitor { + struct list_head list; + struct ubus_client *cl; + uint32_t seq; +}; + +static void +ubusd_monitor_free(struct ubus_monitor *m) +{ + list_del(&m->list); + free(m); +} + +static void +ubusd_monitor_connect(struct ubus_client *cl, struct ubus_msg_buf *ub) +{ + struct ubus_monitor *m; + + ubusd_monitor_disconnect(cl); + + m = calloc(1, sizeof(*m)); + m->cl = cl; + list_add(&m->list, &monitors); +} + +void +ubusd_monitor_disconnect(struct ubus_client *cl) +{ + struct ubus_monitor *m; + + list_for_each_entry(m, &monitors, list) { + if (m->cl != cl) + continue; + + ubusd_monitor_free(m); + return; + } +} + +void +ubusd_monitor_message(struct ubus_client *cl, struct ubus_msg_buf *ub, bool send) +{ + static struct blob_buf mb; + struct ubus_monitor *m; + + if (list_empty(&monitors)) + return; + + blob_buf_init(&mb, 0); + blob_put_int32(&mb, UBUS_MONITOR_CLIENT, cl->id.id); + blob_put_int32(&mb, UBUS_MONITOR_PEER, ub->hdr.peer); + blob_put_int32(&mb, UBUS_MONITOR_SEQ, ub->hdr.seq); + blob_put_int32(&mb, UBUS_MONITOR_TYPE, ub->hdr.type); + blob_put_int8(&mb, UBUS_MONITOR_SEND, send); + blob_put(&mb, UBUS_MONITOR_DATA, blob_data(ub->data), blob_len(ub->data)); + + list_for_each_entry(m, &monitors, list) { + ub = ubus_msg_new(mb.head, blob_raw_len(mb.head), true); + ub->hdr.type = UBUS_MSG_MONITOR; + ub->hdr.seq = ++m->seq; + ubus_msg_send(m->cl, ub, true); + } +} + +static int +ubusd_monitor_recv(struct ubus_client *cl, struct ubus_msg_buf *ub, + const char *method, struct blob_attr *msg) +{ + /* Only root is allowed for now */ + if (cl->uid != 0 || cl->gid != 0) + return UBUS_STATUS_PERMISSION_DENIED; + + if (!strcmp(method, "add")) { + ubusd_monitor_connect(cl, ub); + return 0; + } + + if (!strcmp(method, "remove")) { + ubusd_monitor_disconnect(cl); + return 0; + } + + return UBUS_STATUS_METHOD_NOT_FOUND; +} + +void +ubusd_monitor_init(void) +{ + monitor_obj = ubusd_create_object_internal(NULL, UBUS_SYSTEM_OBJECT_MONITOR); + if (monitor_obj != NULL) + monitor_obj->recv_msg = ubusd_monitor_recv; +} diff --git a/src/3P/ubus/ubusd_obj.c b/src/3P/ubus/ubusd_obj.c new file mode 100644 index 00000000..08314732 --- /dev/null +++ b/src/3P/ubus/ubusd_obj.c @@ -0,0 +1,236 @@ +/* + * Copyright (C) 2011 Felix Fietkau + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#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_tail(&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)); + if (!type) + return NULL; + + type->refcount = 1; + + if (!ubus_alloc_id(&obj_types, &type->id, 0)) + 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_internal(struct ubus_object_type *type, uint32_t id) +{ + struct ubus_object *obj; + + obj = calloc(1, sizeof(*obj)); + if (!obj) + return NULL; + + if (!ubus_alloc_id(&objects, &obj->id, id)) + goto error_free; + + obj->type = type; + INIT_LIST_HEAD(&obj->list); + INIT_LIST_HEAD(&obj->events); + INIT_LIST_HEAD(&obj->subscribers); + INIT_LIST_HEAD(&obj->target_list); + if (type) + type->refcount++; + + return obj; + +error_free: + free(obj); + return NULL; +} + +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_u32(attr[UBUS_ATTR_OBJTYPE])); + else if (attr[UBUS_ATTR_SIGNATURE]) + type = ubus_create_obj_type(attr[UBUS_ATTR_SIGNATURE]); + + obj = ubusd_create_object_internal(type, 0); + if (type) + ubus_unref_object_type(type); + + if (!obj) + return NULL; + + if (attr[UBUS_ATTR_OBJPATH]) { + if (ubusd_acl_check(cl, blob_data(attr[UBUS_ATTR_OBJPATH]), NULL, UBUS_ACL_PUBLISH)) + goto free; + + obj->path.key = strdup(blob_data(attr[UBUS_ATTR_OBJPATH])); + if (!obj->path.key) + goto free; + + if (avl_insert(&path, &obj->path) != 0) { + free((void *) obj->path.key); + obj->path.key = NULL; + goto free; + } + ubusd_send_obj_event(obj, true); + } + + obj->client = cl; + list_add(&obj->list, &cl->objects); + + return obj; + +free: + ubusd_free_object(obj); + return NULL; +} + +void ubus_subscribe(struct ubus_object *obj, struct ubus_object *target) +{ + struct ubus_subscription *s; + bool first = list_empty(&target->subscribers); + + s = calloc(1, sizeof(*s)); + if (!s) + return; + + s->subscriber = obj; + s->target = target; + list_add(&s->list, &target->subscribers); + list_add(&s->target_list, &obj->target_list); + + if (first) + ubus_notify_subscription(target); +} + +void ubus_unsubscribe(struct ubus_subscription *s) +{ + struct ubus_object *obj = s->target; + + list_del(&s->list); + list_del(&s->target_list); + free(s); + + if (list_empty(&obj->subscribers)) + ubus_notify_subscription(obj); +} + +void ubusd_free_object(struct ubus_object *obj) +{ + struct ubus_subscription *s, *tmp; + + list_for_each_entry_safe(s, tmp, &obj->target_list, target_list) { + ubus_unsubscribe(s); + } + list_for_each_entry_safe(s, tmp, &obj->subscribers, list) { + ubus_notify_unsubscribe(s); + } + + ubusd_event_cleanup_object(obj); + if (obj->path.key) { + ubusd_send_obj_event(obj, false); + avl_delete(&path, &obj->path); + free((void *) obj->path.key); + } + if (!list_empty(&obj->list)) + list_del(&obj->list); + ubus_free_id(&objects, &obj->id); + if (obj->type) + ubus_unref_object_type(obj->type); + free(obj); +} + +static void __constructor ubusd_obj_init(void) +{ + ubus_init_id_tree(&objects); + ubus_init_id_tree(&obj_types); + ubus_init_string_tree(&path, false); + ubusd_event_init(); + ubusd_acl_init(); + ubusd_monitor_init(); +} diff --git a/src/3P/ubus/ubusd_obj.h b/src/3P/ubus/ubusd_obj.h new file mode 100644 index 00000000..5ed5ba8f --- /dev/null +++ b/src/3P/ubus/ubusd_obj.h @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2011 Felix Fietkau + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#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_subscription { + struct list_head list, target_list; + struct ubus_object *subscriber, *target; +}; + +struct ubus_object { + struct ubus_id id; + struct list_head list; + + struct list_head events; + + struct list_head subscribers, target_list; + + struct ubus_object_type *type; + struct avl_node path; + + struct ubus_client *client; + int (*recv_msg)(struct ubus_client *client, struct ubus_msg_buf *ub, + const char *method, struct blob_attr *msg); + + int event_seen; + unsigned int invoke_seq; +}; + +struct ubus_object *ubusd_create_object(struct ubus_client *cl, struct blob_attr **attr); +struct ubus_object *ubusd_create_object_internal(struct ubus_object_type *type, uint32_t id); +void ubusd_free_object(struct ubus_object *obj); + +static inline struct ubus_object *ubusd_find_object(uint32_t objid) +{ + struct ubus_object *obj; + struct ubus_id *id; + + id = ubus_find_id(&objects, objid); + if (!id) + return NULL; + + obj = container_of(id, struct ubus_object, id); + return obj; +} + +void ubus_subscribe(struct ubus_object *obj, struct ubus_object *target); +void ubus_unsubscribe(struct ubus_subscription *s); +void ubus_notify_unsubscribe(struct ubus_subscription *s); +void ubus_notify_subscription(struct ubus_object *obj); + +#endif diff --git a/src/3P/ubus/ubusd_proto.c b/src/3P/ubus/ubusd_proto.c new file mode 100644 index 00000000..0af11f27 --- /dev/null +++ b/src/3P/ubus/ubusd_proto.c @@ -0,0 +1,554 @@ +/* + * Copyright (C) 2011-2014 Felix Fietkau + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include + +#include "ubusd.h" + +struct blob_buf b; +static struct ubus_msg_buf *retmsg; +static int *retmsg_data; +static struct avl_tree clients; + +static struct blob_attr *attrbuf[UBUS_ATTR_MAX]; + +typedef int (*ubus_cmd_cb)(struct ubus_client *cl, struct ubus_msg_buf *ub, struct blob_attr **attr); + +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 }, + [UBUS_ATTR_OBJID] = { .type = BLOB_ATTR_INT32 }, + [UBUS_ATTR_STATUS] = { .type = BLOB_ATTR_INT32 }, + [UBUS_ATTR_METHOD] = { .type = BLOB_ATTR_STRING }, + [UBUS_ATTR_USER] = { .type = BLOB_ATTR_STRING }, + [UBUS_ATTR_GROUP] = { .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_close_fd(struct ubus_msg_buf *ub) +{ + if (ub->fd < 0) + return; + + close(ub->fd); + ub->fd = -1; +} + +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_from_blob(shared); + if (!new) + return NULL; + + ubus_msg_init(new, UBUS_MSG_DATA, ub->hdr.seq, ub->hdr.peer); + return new; +} + +void +ubus_proto_send_msg_from_blob(struct ubus_client *cl, struct ubus_msg_buf *ub, + uint8_t type) +{ + ub = ubus_reply_from_blob(ub, true); + if (!ub) + return; + + ub->hdr.type = type; + ubus_msg_send(cl, ub, true); +} + +static 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, true); + return true; +} + +static int ubusd_send_pong(struct ubus_client *cl, struct ubus_msg_buf *ub, struct blob_attr **attr) +{ + ub->hdr.type = UBUS_MSG_DATA; + ubus_msg_send(cl, ub, false); + return 0; +} + +static int ubusd_handle_remove_object(struct ubus_client *cl, struct ubus_msg_buf *ub, struct blob_attr **attr) +{ + struct ubus_object *obj; + + if (!attr[UBUS_ATTR_OBJID]) + return UBUS_STATUS_INVALID_ARGUMENT; + + obj = ubusd_find_object(blob_get_u32(attr[UBUS_ATTR_OBJID])); + if (!obj) + return UBUS_STATUS_NOT_FOUND; + + if (obj->client != cl) + return UBUS_STATUS_PERMISSION_DENIED; + + blob_buf_init(&b, 0); + blob_put_int32(&b, UBUS_ATTR_OBJID, obj->id.id); + + /* check if we're removing the object type as well */ + if (obj->type && obj->type->refcount == 1) + blob_put_int32(&b, UBUS_ATTR_OBJTYPE, obj->type->id.id); + + ubusd_free_object(obj); + ubus_proto_send_msg_from_blob(cl, ub, UBUS_MSG_DATA); + + return 0; +} + +static int ubusd_handle_add_object(struct ubus_client *cl, struct ubus_msg_buf *ub, struct blob_attr **attr) +{ + struct ubus_object *obj; + + 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); + + ubus_proto_send_msg_from_blob(cl, ub, UBUS_MSG_DATA); + return 0; +} + +static void ubusd_send_obj(struct ubus_client *cl, struct ubus_msg_buf *ub, struct ubus_object *obj) +{ + struct ubus_method *m; + int cnt = 0; + void *s; + + blob_buf_init(&b, 0); + + blob_put_string(&b, UBUS_ATTR_OBJPATH, obj->path.key); + blob_put_int32(&b, UBUS_ATTR_OBJID, obj->id.id); + blob_put_int32(&b, UBUS_ATTR_OBJTYPE, obj->type->id.id); + + s = blob_nest_start(&b, UBUS_ATTR_SIGNATURE); + list_for_each_entry(m, &obj->type->methods, list) { + if (!ubusd_acl_check(cl, obj->path.key, blobmsg_name(m->data), UBUS_ACL_ACCESS)) { + blobmsg_add_blob(&b, m->data); + cnt++; + } + } + blob_nest_end(&b, s); + + if (cnt) + ubus_proto_send_msg_from_blob(cl, ub, UBUS_MSG_DATA); +} + +static int ubusd_handle_lookup(struct ubus_client *cl, struct ubus_msg_buf *ub, struct blob_attr **attr) +{ + struct ubus_object *obj; + char *objpath; + bool found = false; + int len; + + 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; + + 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 void +ubusd_forward_invoke(struct ubus_client *cl, struct ubus_object *obj, + const char *method, struct ubus_msg_buf *ub, + struct blob_attr *data) +{ + blob_put_int32(&b, UBUS_ATTR_OBJID, obj->id.id); + blob_put_string(&b, UBUS_ATTR_METHOD, method); + if (cl->user) + blob_put_string(&b, UBUS_ATTR_USER, cl->user); + if (cl->group) + blob_put_string(&b, UBUS_ATTR_GROUP, cl->group); + if (data) + blob_put(&b, UBUS_ATTR_DATA, blob_data(data), blob_len(data)); + + ubus_proto_send_msg_from_blob(obj->client, ub, UBUS_MSG_INVOKE); +} + +static int ubusd_handle_invoke(struct ubus_client *cl, struct ubus_msg_buf *ub, struct blob_attr **attr) +{ + struct ubus_object *obj = NULL; + struct ubus_id *id; + const char *method; + + if (!attr[UBUS_ATTR_METHOD] || !attr[UBUS_ATTR_OBJID]) + return UBUS_STATUS_INVALID_ARGUMENT; + + id = ubus_find_id(&objects, blob_get_u32(attr[UBUS_ATTR_OBJID])); + if (!id) + return UBUS_STATUS_NOT_FOUND; + + obj = container_of(id, struct ubus_object, id); + + method = blob_data(attr[UBUS_ATTR_METHOD]); + + if (ubusd_acl_check(cl, obj->path.key, method, UBUS_ACL_ACCESS)) + return UBUS_STATUS_PERMISSION_DENIED; + + if (!obj->client) + return obj->recv_msg(cl, ub, method, attr[UBUS_ATTR_DATA]); + + ub->hdr.peer = cl->id.id; + blob_buf_init(&b, 0); + + ubusd_forward_invoke(cl, obj, method, ub, attr[UBUS_ATTR_DATA]); + ubus_msg_free(ub); + + return -1; +} + +static int ubusd_handle_notify(struct ubus_client *cl, struct ubus_msg_buf *ub, struct blob_attr **attr) +{ + struct ubus_object *obj = NULL; + struct ubus_subscription *s; + struct ubus_id *id; + const char *method; + bool no_reply = false; + void *c; + + if (!attr[UBUS_ATTR_METHOD] || !attr[UBUS_ATTR_OBJID]) + return UBUS_STATUS_INVALID_ARGUMENT; + + if (attr[UBUS_ATTR_NO_REPLY]) + no_reply = blob_get_int8(attr[UBUS_ATTR_NO_REPLY]); + + id = ubus_find_id(&objects, blob_get_u32(attr[UBUS_ATTR_OBJID])); + if (!id) + return UBUS_STATUS_NOT_FOUND; + + obj = container_of(id, struct ubus_object, id); + if (obj->client != cl) + return UBUS_STATUS_PERMISSION_DENIED; + + if (!no_reply) { + blob_buf_init(&b, 0); + blob_put_int32(&b, UBUS_ATTR_OBJID, id->id); + c = blob_nest_start(&b, UBUS_ATTR_SUBSCRIBERS); + list_for_each_entry(s, &obj->subscribers, list) { + blob_put_int32(&b, 0, s->subscriber->id.id); + } + blob_nest_end(&b, c); + blob_put_int32(&b, UBUS_ATTR_STATUS, 0); + ubus_proto_send_msg_from_blob(cl, ub, UBUS_MSG_STATUS); + } + + ub->hdr.peer = cl->id.id; + method = blob_data(attr[UBUS_ATTR_METHOD]); + list_for_each_entry(s, &obj->subscribers, list) { + blob_buf_init(&b, 0); + if (no_reply) + blob_put_int8(&b, UBUS_ATTR_NO_REPLY, 1); + ubusd_forward_invoke(cl, s->subscriber, method, ub, attr[UBUS_ATTR_DATA]); + } + ubus_msg_free(ub); + + return -1; +} + +static 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 int ubusd_handle_response(struct ubus_client *cl, struct ubus_msg_buf *ub, struct blob_attr **attr) +{ + struct ubus_object *obj; + + if (!attr[UBUS_ATTR_OBJID] || + (ub->hdr.type == UBUS_MSG_STATUS && !attr[UBUS_ATTR_STATUS]) || + (ub->hdr.type == UBUS_MSG_DATA && !attr[UBUS_ATTR_DATA])) + goto error; + + obj = ubusd_find_object(blob_get_u32(attr[UBUS_ATTR_OBJID])); + if (!obj) + goto error; + + if (cl != obj->client) + goto error; + + cl = ubusd_get_client_by_id(ub->hdr.peer); + if (!cl) + goto error; + + ub->hdr.peer = blob_get_u32(attr[UBUS_ATTR_OBJID]); + ubus_msg_send(cl, ub, true); + return -1; + +error: + ubus_msg_free(ub); + return -1; +} + +static int ubusd_handle_add_watch(struct ubus_client *cl, struct ubus_msg_buf *ub, struct blob_attr **attr) +{ + struct ubus_object *obj, *target; + + if (!attr[UBUS_ATTR_OBJID] || !attr[UBUS_ATTR_TARGET]) + return UBUS_STATUS_INVALID_ARGUMENT; + + obj = ubusd_find_object(blob_get_u32(attr[UBUS_ATTR_OBJID])); + if (!obj) + return UBUS_STATUS_NOT_FOUND; + + if (cl != obj->client) + return UBUS_STATUS_INVALID_ARGUMENT; + + target = ubusd_find_object(blob_get_u32(attr[UBUS_ATTR_TARGET])); + if (!target) + return UBUS_STATUS_NOT_FOUND; + + if (cl == target->client) + return UBUS_STATUS_INVALID_ARGUMENT; + + if (!target->path.key) { + if (strcmp(target->client->user, cl->user) && strcmp(target->client->group, cl->group)) + return UBUS_STATUS_NOT_FOUND; + } else if (ubusd_acl_check(cl, target->path.key, NULL, UBUS_ACL_SUBSCRIBE)) { + return UBUS_STATUS_NOT_FOUND; + } + + ubus_subscribe(obj, target); + return 0; +} + +static int ubusd_handle_remove_watch(struct ubus_client *cl, struct ubus_msg_buf *ub, struct blob_attr **attr) +{ + struct ubus_object *obj; + struct ubus_subscription *s; + uint32_t id; + + if (!attr[UBUS_ATTR_OBJID] || !attr[UBUS_ATTR_TARGET]) + return UBUS_STATUS_INVALID_ARGUMENT; + + obj = ubusd_find_object(blob_get_u32(attr[UBUS_ATTR_OBJID])); + if (!obj) + return UBUS_STATUS_NOT_FOUND; + + if (cl != obj->client) + return UBUS_STATUS_INVALID_ARGUMENT; + + id = blob_get_u32(attr[UBUS_ATTR_TARGET]); + list_for_each_entry(s, &obj->target_list, target_list) { + if (s->target->id.id != id) + continue; + + ubus_unsubscribe(s); + return 0; + } + + return UBUS_STATUS_NOT_FOUND; +} + +static const ubus_cmd_cb handlers[__UBUS_MSG_LAST] = { + [UBUS_MSG_PING] = ubusd_send_pong, + [UBUS_MSG_ADD_OBJECT] = ubusd_handle_add_object, + [UBUS_MSG_REMOVE_OBJECT] = ubusd_handle_remove_object, + [UBUS_MSG_LOOKUP] = ubusd_handle_lookup, + [UBUS_MSG_INVOKE] = ubusd_handle_invoke, + [UBUS_MSG_STATUS] = ubusd_handle_response, + [UBUS_MSG_DATA] = ubusd_handle_response, + [UBUS_MSG_SUBSCRIBE] = ubusd_handle_add_watch, + [UBUS_MSG_UNSUBSCRIBE] = ubusd_handle_remove_watch, + [UBUS_MSG_NOTIFY] = ubusd_handle_notify, +}; + +void ubusd_proto_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 (ub->hdr.type != UBUS_MSG_STATUS) + ubus_msg_close_fd(ub); + + if (cb) + ret = cb(cl, ub, ubus_parse_msg(ub->data)); + else + ret = UBUS_STATUS_INVALID_COMMAND; + + if (ret == -1) + return; + + ubus_msg_free(ub); + + *retmsg_data = htonl(ret); + ubus_msg_send(cl, retmsg, false); +} + +struct ubus_client *ubusd_proto_new_client(int fd, uloop_fd_handler cb) +{ + struct ubus_client *cl; + + cl = calloc(1, sizeof(*cl)); + if (!cl) + return NULL; + + if (ubusd_acl_init_client(cl, fd)) + goto free; + + INIT_LIST_HEAD(&cl->objects); + cl->sock.fd = fd; + cl->sock.cb = cb; + cl->pending_msg_fd = -1; + + if (!ubus_alloc_id(&clients, &cl->id, 0)) + goto free; + + if (!ubusd_send_hello(cl)) + goto delete; + + return cl; + +delete: + ubus_free_id(&clients, &cl->id); +free: + free(cl); + return NULL; +} + +void ubusd_proto_free_client(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); + } + + ubusd_acl_free_client(cl); + ubus_free_id(&clients, &cl->id); +} + +void ubus_notify_subscription(struct ubus_object *obj) +{ + bool active = !list_empty(&obj->subscribers); + struct ubus_msg_buf *ub; + + blob_buf_init(&b, 0); + blob_put_int32(&b, UBUS_ATTR_OBJID, obj->id.id); + blob_put_int8(&b, UBUS_ATTR_ACTIVE, active); + + ub = ubus_msg_from_blob(false); + if (!ub) + return; + + ubus_msg_init(ub, UBUS_MSG_NOTIFY, ++obj->invoke_seq, 0); + ubus_msg_send(obj->client, ub, true); +} + +void ubus_notify_unsubscribe(struct ubus_subscription *s) +{ + struct ubus_msg_buf *ub; + + blob_buf_init(&b, 0); + blob_put_int32(&b, UBUS_ATTR_OBJID, s->subscriber->id.id); + blob_put_int32(&b, UBUS_ATTR_TARGET, s->target->id.id); + + ub = ubus_msg_from_blob(false); + if (ub != NULL) { + ubus_msg_init(ub, UBUS_MSG_UNSUBSCRIBE, ++s->subscriber->invoke_seq, 0); + ubus_msg_send(s->subscriber->client, ub, true); + } + + ubus_unsubscribe(s); +} + +static void __constructor ubusd_proto_init(void) +{ + ubus_init_id_tree(&clients); + + 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/src/3P/ubus/ubusmsg.h b/src/3P/ubus/ubusmsg.h new file mode 100644 index 00000000..b95c4725 --- /dev/null +++ b/src/3P/ubus/ubusmsg.h @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2011 Felix Fietkau + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __UBUSMSG_H +#define __UBUSMSG_H + +#include +#include + +#define __packetdata __attribute__((packed)) __attribute__((__aligned__(4))) + +//AWOX REDUCE SIZE. #define UBUS_MSG_CHUNK_SIZE 65536 +#define UBUS_MSG_CHUNK_SIZE 16384 + +#define UBUS_SYSTEM_OBJECT_EVENT 1 +#define UBUS_SYSTEM_OBJECT_ACL 2 +#define UBUS_SYSTEM_OBJECT_MONITOR 3 +#define UBUS_SYSTEM_OBJECT_MAX 1024 + +struct ubus_msghdr { + uint8_t version; + uint8_t type; + uint16_t seq; + uint32_t peer; +} __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, + + UBUS_MSG_ADD_OBJECT, + UBUS_MSG_REMOVE_OBJECT, + + /* + * subscribe/unsubscribe to object notifications + * The unsubscribe message is sent from ubusd when + * the object disappears + */ + UBUS_MSG_SUBSCRIBE, + UBUS_MSG_UNSUBSCRIBE, + + /* + * send a notification to all subscribers of an object. + * when sent from the server, it indicates a subscription + * status change + */ + UBUS_MSG_NOTIFY, + + UBUS_MSG_MONITOR, + + /* 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, + UBUS_ATTR_TARGET, + + UBUS_ATTR_ACTIVE, + UBUS_ATTR_NO_REPLY, + + UBUS_ATTR_SUBSCRIBERS, + + UBUS_ATTR_USER, + UBUS_ATTR_GROUP, + + /* must be last */ + UBUS_ATTR_MAX, +}; + +enum ubus_monitor_attr { + UBUS_MONITOR_CLIENT, + UBUS_MONITOR_PEER, + UBUS_MONITOR_SEND, + UBUS_MONITOR_SEQ, + UBUS_MONITOR_TYPE, + UBUS_MONITOR_DATA, + + /* must be last */ + UBUS_MONITOR_MAX, +}; + +enum ubus_msg_status { + UBUS_STATUS_OK, + UBUS_STATUS_INVALID_COMMAND, + UBUS_STATUS_INVALID_ARGUMENT, + UBUS_STATUS_METHOD_NOT_FOUND, + UBUS_STATUS_NOT_FOUND, + UBUS_STATUS_NO_DATA, + UBUS_STATUS_PERMISSION_DENIED, + UBUS_STATUS_TIMEOUT, + UBUS_STATUS_NOT_SUPPORTED, + UBUS_STATUS_UNKNOWN_ERROR, + UBUS_STATUS_CONNECTION_FAILED, + __UBUS_STATUS_LAST +}; + +#endif diff --git a/src/3P/ubus/ucli/builders/cmake/CMakeLists.txt b/src/3P/ubus/ucli/builders/cmake/CMakeLists.txt new file mode 100644 index 00000000..57ea624d --- /dev/null +++ b/src/3P/ubus/ucli/builders/cmake/CMakeLists.txt @@ -0,0 +1,30 @@ +cmake_minimum_required(VERSION 3.0) + +project (ubus-cli) + +set (CMAKE_MODULE_PATH "${MODULE_PATH}") + +set(DISABLE_TARGET_OPTIMIZATION ON) + +include (aw) + +include_directories ($ENV{AWOXCVS}/AwoxAudio/Libs/External/ubus/) + +ADD_DEFINITIONS (-Werror --std=gnu99 -Wmissing-declarations) + +ADD_DEFINITIONS (-DUBUS_MAX_MSGLEN=1048576) +ADD_DEFINITIONS (-DUBUS_UNIX_SOCKET="/tmp/ubus.sock") + +file ( + GLOB_RECURSE + source_files + + $ENV{AWOXCVS}/AwoxAudio/Libs/External/ubus/cli.c + ) + +# Daemon +add_executable (ucli ${source_files}) +set_target_properties (ucli PROPERTIES OUTPUT_NAME ubus) +target_link_libraries (ucli LINK_PUBLIC ubox ubus blobmsg_json) + +install (TARGETS ucli RUNTIME DESTINATION bin)