Bump ubus-2016-07-02-053be7df871e05478284235732f8b0608089512f
This commit is contained in:
10
src/3P/ubus/.gitignore
vendored
Normal file
10
src/3P/ubus/.gitignore
vendored
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
Makefile
|
||||||
|
CMakeCache.txt
|
||||||
|
CMakeFiles
|
||||||
|
*.cmake
|
||||||
|
*.a
|
||||||
|
*.so
|
||||||
|
*.dylib
|
||||||
|
examples/server
|
||||||
|
examples/client
|
||||||
|
install_manifest.txt
|
||||||
55
src/3P/ubus/CMakeLists.txt
Normal file
55
src/3P/ubus/CMakeLists.txt
Normal file
@@ -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)
|
||||||
587
src/3P/ubus/cli.c
Normal file
587
src/3P/ubus/cli.c
Normal file
@@ -0,0 +1,587 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2011 Felix Fietkau <nbd@openwrt.org>
|
||||||
|
*
|
||||||
|
* 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 <unistd.h>
|
||||||
|
|
||||||
|
#include <libubox/blobmsg_json.h>
|
||||||
|
#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 [<options>] <command> [arguments...]\n"
|
||||||
|
"Options:\n"
|
||||||
|
" -s <socket>: Set the unix domain socket to connect to\n"
|
||||||
|
" -t <timeout>: Set the timeout (in seconds) for a command to complete\n"
|
||||||
|
" -S: Use simplified output (for scripts)\n"
|
||||||
|
" -v: More verbose output\n"
|
||||||
|
" -m <type>: (for monitor): include a specific message type\n"
|
||||||
|
" (can be used more than once)\n"
|
||||||
|
" -M <r|t> (for monitor): only capture received or transmitted traffic\n"
|
||||||
|
"\n"
|
||||||
|
"Commands:\n"
|
||||||
|
" - list [<path>] List objects\n"
|
||||||
|
" - call <path> <method> [<message>] Call an object method\n"
|
||||||
|
" - listen [<path>...] Listen for events\n"
|
||||||
|
" - send <type> [<message>] Send an event\n"
|
||||||
|
" - wait_for <object> [<object>...] 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;
|
||||||
|
}
|
||||||
12
src/3P/ubus/examples/CMakeLists.txt
Normal file
12
src/3P/ubus/examples/CMakeLists.txt
Normal file
@@ -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()
|
||||||
242
src/3P/ubus/examples/client.c
Normal file
242
src/3P/ubus/examples/client.c
Normal file
@@ -0,0 +1,242 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2011 Felix Fietkau <nbd@openwrt.org>
|
||||||
|
*
|
||||||
|
* 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 <sys/time.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include <libubox/ustream.h>
|
||||||
|
|
||||||
|
#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;
|
||||||
|
}
|
||||||
48
src/3P/ubus/examples/count.c
Normal file
48
src/3P/ubus/examples/count.c
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2011 Felix Fietkau <nbd@openwrt.org>
|
||||||
|
*
|
||||||
|
* 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 <stdio.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#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;
|
||||||
|
}
|
||||||
19
src/3P/ubus/examples/count.h
Normal file
19
src/3P/ubus/examples/count.h
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2011 Felix Fietkau <nbd@openwrt.org>
|
||||||
|
*
|
||||||
|
* 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
|
||||||
263
src/3P/ubus/examples/server.c
Normal file
263
src/3P/ubus/examples/server.c
Normal file
@@ -0,0 +1,263 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2011-2014 Felix Fietkau <nbd@openwrt.org>
|
||||||
|
*
|
||||||
|
* 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 <unistd.h>
|
||||||
|
#include <signal.h>
|
||||||
|
|
||||||
|
#include <libubox/blobmsg_json.h>
|
||||||
|
#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;
|
||||||
|
}
|
||||||
153
src/3P/ubus/libubus-acl.c
Normal file
153
src/3P/ubus/libubus-acl.c
Normal file
@@ -0,0 +1,153 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2015 John Cripin <blogic@openwrt.org>
|
||||||
|
*
|
||||||
|
* 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 <unistd.h>
|
||||||
|
|
||||||
|
#include <libubox/blob.h>
|
||||||
|
#include <libubox/blobmsg.h>
|
||||||
|
|
||||||
|
#include "libubus.h"
|
||||||
|
#include <libubox/avl-cmp.h>
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
32
src/3P/ubus/libubus-internal.h
Normal file
32
src/3P/ubus/libubus-internal.h
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2011-2014 Felix Fietkau <nbd@openwrt.org>
|
||||||
|
*
|
||||||
|
* 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
|
||||||
412
src/3P/ubus/libubus-io.c
Normal file
412
src/3P/ubus/libubus-io.c
Normal file
@@ -0,0 +1,412 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2011-2014 Felix Fietkau <nbd@openwrt.org>
|
||||||
|
*
|
||||||
|
* 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 <sys/types.h>
|
||||||
|
#include <sys/uio.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <poll.h>
|
||||||
|
|
||||||
|
#include <libubox/usock.h>
|
||||||
|
#include <libubox/blob.h>
|
||||||
|
#include <libubox/blobmsg.h>
|
||||||
|
|
||||||
|
#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;
|
||||||
|
}
|
||||||
261
src/3P/ubus/libubus-obj.c
Normal file
261
src/3P/ubus/libubus-obj.c
Normal file
@@ -0,0 +1,261 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2011-2012 Felix Fietkau <nbd@openwrt.org>
|
||||||
|
*
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
478
src/3P/ubus/libubus-req.c
Normal file
478
src/3P/ubus/libubus-req.c
Normal file
@@ -0,0 +1,478 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2011-2014 Felix Fietkau <nbd@openwrt.org>
|
||||||
|
*
|
||||||
|
* 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 <unistd.h>
|
||||||
|
#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);
|
||||||
|
}
|
||||||
69
src/3P/ubus/libubus-sub.c
Normal file
69
src/3P/ubus/libubus-sub.c
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2011-2012 Felix Fietkau <nbd@openwrt.org>
|
||||||
|
*
|
||||||
|
* 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);
|
||||||
|
}
|
||||||
|
|
||||||
373
src/3P/ubus/libubus.c
Normal file
373
src/3P/ubus/libubus.c
Normal file
@@ -0,0 +1,373 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2011-2014 Felix Fietkau <nbd@openwrt.org>
|
||||||
|
*
|
||||||
|
* 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 <sys/types.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include <libubox/blob.h>
|
||||||
|
#include <libubox/blobmsg.h>
|
||||||
|
|
||||||
|
#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);
|
||||||
|
}
|
||||||
390
src/3P/ubus/libubus.h
Normal file
390
src/3P/ubus/libubus.h
Normal file
@@ -0,0 +1,390 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2011-2014 Felix Fietkau <nbd@openwrt.org>
|
||||||
|
*
|
||||||
|
* 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 <libubox/avl.h>
|
||||||
|
#include <libubox/list.h>
|
||||||
|
#include <libubox/blobmsg.h>
|
||||||
|
#include <libubox/uloop.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#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
|
||||||
43
src/3P/ubus/libubus/builders/cmake/CMakeLists.txt
Normal file
43
src/3P/ubus/libubus/builders/cmake/CMakeLists.txt
Normal file
@@ -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)
|
||||||
52
src/3P/ubus/lua/CMakeLists.txt
Normal file
52
src/3P/ubus/lua/CMakeLists.txt
Normal file
@@ -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()
|
||||||
1
src/3P/ubus/lua/builders/linux-gcc/.gitignore
vendored
Normal file
1
src/3P/ubus/lua/builders/linux-gcc/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
_*
|
||||||
60
src/3P/ubus/lua/publisher.lua
Normal file
60
src/3P/ubus/lua/publisher.lua
Normal file
@@ -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()
|
||||||
25
src/3P/ubus/lua/subscriber.lua
Normal file
25
src/3P/ubus/lua/subscriber.lua
Normal file
@@ -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()
|
||||||
54
src/3P/ubus/lua/test.lua
Executable file
54
src/3P/ubus/lua/test.lua
Executable file
@@ -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()
|
||||||
41
src/3P/ubus/lua/test_client.lua
Executable file
41
src/3P/ubus/lua/test_client.lua
Executable file
@@ -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()
|
||||||
990
src/3P/ubus/lua/ubus.c
Normal file
990
src/3P/ubus/lua/ubus.c
Normal file
@@ -0,0 +1,990 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2012 Jo-Philipp Wich <jow@openwrt.org>
|
||||||
|
* Copyright (C) 2012 John Crispin <blogic@openwrt.org>
|
||||||
|
* Copyright (C) 2016 Iain Fraser <iainf@netduma.com>
|
||||||
|
*
|
||||||
|
* 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 <unistd.h>
|
||||||
|
#include <libubus.h>
|
||||||
|
#include <libubox/blobmsg.h>
|
||||||
|
#include <libubox/blobmsg_json.h>
|
||||||
|
#include <lauxlib.h>
|
||||||
|
#include <lua.h>
|
||||||
|
|
||||||
|
#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;
|
||||||
|
}
|
||||||
24
src/3P/ubus/ubus_common.h
Normal file
24
src/3P/ubus/ubus_common.h
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2011 Felix Fietkau <nbd@openwrt.org>
|
||||||
|
*
|
||||||
|
* 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
|
||||||
422
src/3P/ubus/ubusd.c
Normal file
422
src/3P/ubus/ubusd.c
Normal file
@@ -0,0 +1,422 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2011-2014 Felix Fietkau <nbd@openwrt.org>
|
||||||
|
*
|
||||||
|
* 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 <sys/socket.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/uio.h>
|
||||||
|
#ifdef FreeBSD
|
||||||
|
#include <sys/param.h>
|
||||||
|
#endif
|
||||||
|
#include <syslog.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
|
||||||
|
#include <libubox/blob.h>
|
||||||
|
#include <libubox/uloop.h>
|
||||||
|
#include <libubox/usock.h>
|
||||||
|
#include <libubox/list.h>
|
||||||
|
|
||||||
|
#include "ubusd.h"
|
||||||
|
|
||||||
|
static struct 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 [<options>]\n"
|
||||||
|
"Options: \n"
|
||||||
|
" -A <path>: Set the path to ACL files\n"
|
||||||
|
" -s <socket>: 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;
|
||||||
|
}
|
||||||
93
src/3P/ubus/ubusd.h
Normal file
93
src/3P/ubus/ubusd.h
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2011-2014 Felix Fietkau <nbd@openwrt.org>
|
||||||
|
*
|
||||||
|
* 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 <libubox/list.h>
|
||||||
|
#include <libubox/uloop.h>
|
||||||
|
#include <libubox/blobmsg.h>
|
||||||
|
#include "ubus_common.h"
|
||||||
|
#include "ubusd_id.h"
|
||||||
|
#include "ubusd_obj.h"
|
||||||
|
#include "ubusmsg.h"
|
||||||
|
#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
|
||||||
38
src/3P/ubus/ubusd/builders/cmake/CMakeLists.txt
Normal file
38
src/3P/ubus/ubusd/builders/cmake/CMakeLists.txt
Normal file
@@ -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
|
||||||
|
)
|
||||||
495
src/3P/ubus/ubusd_acl.c
Normal file
495
src/3P/ubus/ubusd_acl.c
Normal file
@@ -0,0 +1,495 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2015 John Crispin <blogic@openwrt.org>
|
||||||
|
*
|
||||||
|
* 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 <sys/socket.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
|
||||||
|
#include <syslog.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <glob.h>
|
||||||
|
#include <grp.h>
|
||||||
|
#include <pwd.h>
|
||||||
|
|
||||||
|
#include <libubox/vlist.h>
|
||||||
|
#include <libubox/blobmsg_json.h>
|
||||||
|
#include <libubox/avl-cmp.h>
|
||||||
|
|
||||||
|
#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;
|
||||||
|
}
|
||||||
28
src/3P/ubus/ubusd_acl.h
Normal file
28
src/3P/ubus/ubusd_acl.h
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2015 John Crispin <blogic@openwrt.org>
|
||||||
|
*
|
||||||
|
* 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
|
||||||
271
src/3P/ubus/ubusd_event.c
Normal file
271
src/3P/ubus/ubusd_event.c
Normal file
@@ -0,0 +1,271 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2011 Felix Fietkau <nbd@openwrt.org>
|
||||||
|
*
|
||||||
|
* 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 <arpa/inet.h>
|
||||||
|
#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;
|
||||||
|
}
|
||||||
|
|
||||||
71
src/3P/ubus/ubusd_id.c
Normal file
71
src/3P/ubus/ubusd_id.c
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2011 Felix Fietkau <nbd@openwrt.org>
|
||||||
|
*
|
||||||
|
* 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 <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <libubox/avl-cmp.h>
|
||||||
|
|
||||||
|
#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;
|
||||||
|
}
|
||||||
|
|
||||||
45
src/3P/ubus/ubusd_id.h
Normal file
45
src/3P/ubus/ubusd_id.h
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2011 Felix Fietkau <nbd@openwrt.org>
|
||||||
|
*
|
||||||
|
* 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 <libubox/avl.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
struct ubus_id {
|
||||||
|
struct avl_node avl;
|
||||||
|
uint32_t id;
|
||||||
|
};
|
||||||
|
|
||||||
|
void ubus_init_id_tree(struct avl_tree *tree);
|
||||||
|
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
|
||||||
110
src/3P/ubus/ubusd_monitor.c
Normal file
110
src/3P/ubus/ubusd_monitor.c
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2015 Felix Fietkau <nbd@openwrt.org>
|
||||||
|
*
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
236
src/3P/ubus/ubusd_obj.c
Normal file
236
src/3P/ubus/ubusd_obj.c
Normal file
@@ -0,0 +1,236 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2011 Felix Fietkau <nbd@openwrt.org>
|
||||||
|
*
|
||||||
|
* 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();
|
||||||
|
}
|
||||||
84
src/3P/ubus/ubusd_obj.h
Normal file
84
src/3P/ubus/ubusd_obj.h
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2011 Felix Fietkau <nbd@openwrt.org>
|
||||||
|
*
|
||||||
|
* 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
|
||||||
554
src/3P/ubus/ubusd_proto.c
Normal file
554
src/3P/ubus/ubusd_proto.c
Normal file
@@ -0,0 +1,554 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2011-2014 Felix Fietkau <nbd@openwrt.org>
|
||||||
|
*
|
||||||
|
* 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 <arpa/inet.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#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));
|
||||||
|
}
|
||||||
134
src/3P/ubus/ubusmsg.h
Normal file
134
src/3P/ubus/ubusmsg.h
Normal file
@@ -0,0 +1,134 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2011 Felix Fietkau <nbd@openwrt.org>
|
||||||
|
*
|
||||||
|
* 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 <stdint.h>
|
||||||
|
#include <libubox/blob.h>
|
||||||
|
|
||||||
|
#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
|
||||||
30
src/3P/ubus/ucli/builders/cmake/CMakeLists.txt
Normal file
30
src/3P/ubus/ucli/builders/cmake/CMakeLists.txt
Normal file
@@ -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)
|
||||||
Reference in New Issue
Block a user