├── .gitignore ├── CMakeLists.txt ├── cli.c ├── examples ├── CMakeLists.txt ├── client.c └── server.c ├── libubus-internal.h ├── libubus-io.c ├── libubus-obj.c ├── libubus-req.c ├── libubus-sub.c ├── libubus.c ├── libubus.h ├── lua ├── CMakeLists.txt ├── test.lua ├── test_client.lua └── ubus.c ├── python ├── .gitignore ├── MANIFEST.in ├── setup.py ├── tests │ ├── __init__.py │ ├── test_lib.py │ └── test_session.py ├── tox.ini └── ubus │ ├── __init__.py │ ├── libubus.processed.h │ └── session.py ├── systemd ├── CMakeLists.txt ├── ubus.service.in └── ubus.socket.in ├── ubus_common.h ├── ubusd.c ├── ubusd.h ├── ubusd_event.c ├── ubusd_id.c ├── ubusd_id.h ├── ubusd_obj.c ├── ubusd_obj.h ├── ubusd_proto.c └── ubusmsg.h /.gitignore: -------------------------------------------------------------------------------- 1 | Makefile 2 | CMakeCache.txt 3 | CMakeFiles 4 | *.cmake 5 | *.a 6 | *.so 7 | *.dylib 8 | examples/server 9 | examples/client 10 | ubusd 11 | /ubus 12 | install_manifest.txt 13 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.6) 2 | 3 | PROJECT(ubus C) 4 | ADD_DEFINITIONS(-Os -Wall -Werror --std=gnu99 -g3 -Wmissing-declarations) 5 | 6 | OPTION(BUILD_LUA "build Lua plugin" ON) 7 | OPTION(BUILD_EXAMPLES "build examples" ON) 8 | OPTION(ENABLE_SYSTEMD "systemd support" ON) 9 | 10 | SET(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "") 11 | SET(UBUS_UNIX_SOCKET "/var/run/ubus.sock") 12 | 13 | ADD_DEFINITIONS( -DUBUS_UNIX_SOCKET="${UBUS_UNIX_SOCKET}") 14 | 15 | IF(APPLE) 16 | INCLUDE_DIRECTORIES(/opt/local/include) 17 | LINK_DIRECTORIES(/opt/local/lib) 18 | ENDIF() 19 | 20 | ADD_LIBRARY(ubus SHARED libubus.c libubus-io.c libubus-obj.c libubus-sub.c libubus-req.c) 21 | TARGET_LINK_LIBRARIES(ubus ubox) 22 | 23 | ADD_EXECUTABLE(ubusd ubusd.c ubusd_id.c ubusd_obj.c ubusd_proto.c ubusd_event.c) 24 | TARGET_LINK_LIBRARIES(ubusd ubox) 25 | 26 | find_library(json NAMES json-c json) 27 | ADD_EXECUTABLE(cli cli.c) 28 | SET_TARGET_PROPERTIES(cli PROPERTIES OUTPUT_NAME ubus) 29 | TARGET_LINK_LIBRARIES(cli ubus ubox blobmsg_json ${json}) 30 | 31 | ADD_SUBDIRECTORY(lua) 32 | ADD_SUBDIRECTORY(examples) 33 | 34 | INSTALL(TARGETS ubus cli 35 | LIBRARY DESTINATION lib 36 | RUNTIME DESTINATION bin 37 | ) 38 | INSTALL(TARGETS ubusd 39 | RUNTIME DESTINATION sbin 40 | ) 41 | 42 | INSTALL(FILES ubusmsg.h ubus_common.h libubus.h DESTINATION include) 43 | 44 | # FIXME: this works but certainly can be done better: 45 | SET(UBUSD_BINARY "${CMAKE_INSTALL_PREFIX}/sbin/ubusd") 46 | 47 | # do this after the installs so we have the proper paths 48 | IF(ENABLE_SYSTEMD) 49 | INCLUDE(FindPkgConfig) 50 | PKG_CHECK_MODULES(SYSTEMD systemd) 51 | IF(SYSTEMD_FOUND) 52 | ADD_SUBDIRECTORY(systemd) 53 | ENDIF() 54 | ENDIF() 55 | -------------------------------------------------------------------------------- /cli.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 Felix Fietkau 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License version 2.1 6 | * as published by the Free Software Foundation 7 | * 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU General Public License for more details. 12 | */ 13 | 14 | #include 15 | 16 | #include 17 | #include "libubus.h" 18 | 19 | static struct blob_buf b; 20 | static int timeout = 30; 21 | static bool simple_output = false; 22 | static int verbose = 0; 23 | 24 | static const char *format_type(void *priv, struct blob_attr *attr) 25 | { 26 | static const char * const attr_types[] = { 27 | [BLOBMSG_TYPE_INT8] = "\"Boolean\"", 28 | [BLOBMSG_TYPE_INT32] = "\"Integer\"", 29 | [BLOBMSG_TYPE_STRING] = "\"String\"", 30 | [BLOBMSG_TYPE_ARRAY] = "\"Array\"", 31 | [BLOBMSG_TYPE_TABLE] = "\"Table\"", 32 | }; 33 | const char *type = NULL; 34 | int typeid; 35 | 36 | if (blob_id(attr) != BLOBMSG_TYPE_INT32) 37 | return NULL; 38 | 39 | typeid = blobmsg_get_u32(attr); 40 | if (typeid < ARRAY_SIZE(attr_types)) 41 | type = attr_types[typeid]; 42 | if (!type) 43 | type = "\"(unknown)\""; 44 | 45 | return type; 46 | } 47 | 48 | static void receive_list_result(struct ubus_context *ctx, struct ubus_object_data *obj, void *priv) 49 | { 50 | struct blob_attr *cur; 51 | char *s; 52 | int rem; 53 | 54 | if (simple_output || !verbose) { 55 | printf("%s\n", obj->path); 56 | return; 57 | } 58 | 59 | printf("'%s' @%08x\n", obj->path, obj->id); 60 | 61 | if (!obj->signature) 62 | return; 63 | 64 | blob_for_each_attr(cur, obj->signature, rem) { 65 | s = blobmsg_format_json_with_cb(cur, false, format_type, NULL, -1); 66 | printf("\t%s\n", s); 67 | free(s); 68 | } 69 | } 70 | 71 | static void receive_call_result_data(struct ubus_request *req, int type, struct blob_attr *msg) 72 | { 73 | char *str; 74 | if (!msg) 75 | return; 76 | 77 | str = blobmsg_format_json_indent(msg, true, simple_output ? -1 : 0); 78 | printf("%s\n", str); 79 | free(str); 80 | } 81 | 82 | static void receive_event(struct ubus_context *ctx, struct ubus_event_handler *ev, 83 | const char *type, struct blob_attr *msg) 84 | { 85 | char *str; 86 | 87 | str = blobmsg_format_json(msg, true); 88 | printf("{ \"%s\": %s }\n", type, str); 89 | free(str); 90 | } 91 | 92 | static int ubus_cli_list(struct ubus_context *ctx, int argc, char **argv) 93 | { 94 | const char *path = NULL; 95 | 96 | if (argc > 1) 97 | return -2; 98 | 99 | if (argc == 1) 100 | path = argv[0]; 101 | 102 | return ubus_lookup(ctx, path, receive_list_result, NULL); 103 | } 104 | 105 | static int ubus_cli_call(struct ubus_context *ctx, int argc, char **argv) 106 | { 107 | uint32_t id; 108 | int ret; 109 | 110 | if (argc < 2 || argc > 3) 111 | return -2; 112 | 113 | blob_buf_init(&b, 0); 114 | if (argc == 3 && !blobmsg_add_json_from_string(&b, argv[2])) { 115 | if (!simple_output) 116 | fprintf(stderr, "Failed to parse message data\n"); 117 | return -1; 118 | } 119 | 120 | ret = ubus_lookup_id(ctx, argv[0], &id); 121 | if (ret) 122 | return ret; 123 | 124 | return ubus_invoke(ctx, id, argv[1], b.head, receive_call_result_data, NULL, timeout * 1000); 125 | } 126 | 127 | static int ubus_cli_listen(struct ubus_context *ctx, int argc, char **argv) 128 | { 129 | static struct ubus_event_handler listener; 130 | const char *event; 131 | int ret = 0; 132 | 133 | memset(&listener, 0, sizeof(listener)); 134 | listener.cb = receive_event; 135 | 136 | if (argc > 0) { 137 | event = argv[0]; 138 | } else { 139 | event = "*"; 140 | argc = 1; 141 | } 142 | 143 | do { 144 | ret = ubus_register_event_handler(ctx, &listener, event); 145 | if (ret) 146 | break; 147 | 148 | argv++; 149 | argc--; 150 | if (argc <= 0) 151 | break; 152 | 153 | event = argv[0]; 154 | } while (1); 155 | 156 | if (ret) { 157 | if (!simple_output) 158 | fprintf(stderr, "Error while registering for event '%s': %s\n", 159 | event, ubus_strerror(ret)); 160 | return -1; 161 | } 162 | 163 | uloop_init(); 164 | ubus_add_uloop(ctx); 165 | uloop_run(); 166 | uloop_done(); 167 | 168 | return 0; 169 | } 170 | 171 | static int ubus_cli_send(struct ubus_context *ctx, int argc, char **argv) 172 | { 173 | if (argc < 1 || argc > 2) 174 | return -2; 175 | 176 | blob_buf_init(&b, 0); 177 | 178 | if (argc == 2 && !blobmsg_add_json_from_string(&b, argv[1])) { 179 | if (!simple_output) 180 | fprintf(stderr, "Failed to parse message data\n"); 181 | return -1; 182 | } 183 | 184 | return ubus_send_event(ctx, argv[0], b.head); 185 | } 186 | 187 | static int usage(const char *prog) 188 | { 189 | fprintf(stderr, 190 | "Usage: %s [] [arguments...]\n" 191 | "Options:\n" 192 | " -s : Set the unix domain socket to connect to\n" 193 | " -t : Set the timeout (in seconds) for a command to complete\n" 194 | " -S: Use simplified output (for scripts)\n" 195 | " -v: More verbose output\n" 196 | "\n" 197 | "Commands:\n" 198 | " - list [] List objects\n" 199 | " - call [] Call an object method\n" 200 | " - listen [...] Listen for events\n" 201 | " - send [] Send an event\n" 202 | "\n", prog); 203 | return 1; 204 | } 205 | 206 | 207 | struct { 208 | const char *name; 209 | int (*cb)(struct ubus_context *ctx, int argc, char **argv); 210 | } commands[] = { 211 | { "list", ubus_cli_list }, 212 | { "call", ubus_cli_call }, 213 | { "listen", ubus_cli_listen }, 214 | { "send", ubus_cli_send }, 215 | }; 216 | 217 | int main(int argc, char **argv) 218 | { 219 | const char *progname, *ubus_socket = NULL; 220 | static struct ubus_context *ctx; 221 | char *cmd; 222 | int ret = 0; 223 | int i, ch; 224 | 225 | progname = argv[0]; 226 | 227 | while ((ch = getopt(argc, argv, "vs:t:S")) != -1) { 228 | switch (ch) { 229 | case 's': 230 | ubus_socket = optarg; 231 | break; 232 | case 't': 233 | timeout = atoi(optarg); 234 | break; 235 | case 'S': 236 | simple_output = true; 237 | break; 238 | case 'v': 239 | verbose++; 240 | break; 241 | default: 242 | return usage(progname); 243 | } 244 | } 245 | 246 | argc -= optind; 247 | argv += optind; 248 | 249 | cmd = argv[0]; 250 | if (argc < 1) 251 | return usage(progname); 252 | 253 | ctx = ubus_connect(ubus_socket); 254 | if (!ctx) { 255 | if (!simple_output) 256 | fprintf(stderr, "Failed to connect to ubus\n"); 257 | return -1; 258 | } 259 | 260 | argv++; 261 | argc--; 262 | 263 | ret = -2; 264 | for (i = 0; i < ARRAY_SIZE(commands); i++) { 265 | if (strcmp(commands[i].name, cmd) != 0) 266 | continue; 267 | 268 | ret = commands[i].cb(ctx, argc, argv); 269 | break; 270 | } 271 | 272 | if (ret > 0 && !simple_output) 273 | fprintf(stderr, "Command failed: %s\n", ubus_strerror(ret)); 274 | else if (ret == -2) 275 | usage(progname); 276 | 277 | ubus_free(ctx); 278 | return ret; 279 | } 280 | -------------------------------------------------------------------------------- /examples/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.6) 2 | 3 | ADD_DEFINITIONS(-I..) 4 | INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/..) 5 | 6 | IF (BUILD_EXAMPLES) 7 | ADD_EXECUTABLE(server server.c) 8 | TARGET_LINK_LIBRARIES(server ubus ubox blobmsg_json) 9 | 10 | ADD_EXECUTABLE(client client.c) 11 | TARGET_LINK_LIBRARIES(client ubus ubox) 12 | ENDIF() 13 | -------------------------------------------------------------------------------- /examples/client.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 Felix Fietkau 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License version 2.1 6 | * as published by the Free Software Foundation 7 | * 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU General Public License for more details. 12 | */ 13 | 14 | #include 15 | #include 16 | 17 | #include "libubus.h" 18 | 19 | static struct ubus_context *ctx; 20 | static struct blob_buf b; 21 | 22 | static void test_client_subscribe_cb(struct ubus_context *ctx, struct ubus_object *obj) 23 | { 24 | printf("test_client_subscribe_cb Start\n"); 25 | fprintf(stderr, "Subscribers active: %d\n", obj->has_subscribers); 26 | printf("test_client_subscribe_cb End\n"); 27 | } 28 | 29 | static struct ubus_object test_client_object = { 30 | .subscribe_cb = test_client_subscribe_cb, 31 | }; 32 | 33 | static void test_client_notify_cb(struct uloop_timeout *timeout) 34 | { 35 | printf("test_client_notify_cb Start\n"); 36 | static int counter = 0; 37 | int err; 38 | struct timeval tv1, tv2; 39 | int max = 1000; 40 | long delta; 41 | 42 | blob_buf_init(&b, 0); 43 | blobmsg_add_u32(&b, "counter", counter++); 44 | 45 | gettimeofday(&tv1, NULL); 46 | err = ubus_notify(ctx, &test_client_object, "ping", b.head, 1000); 47 | gettimeofday(&tv2, NULL); 48 | if (err) 49 | fprintf(stderr, "Notify failed: %s\n", ubus_strerror(err)); 50 | 51 | delta = (tv2.tv_sec - tv1.tv_sec) * 1000000 + (tv2.tv_usec - tv1.tv_usec); 52 | fprintf(stderr, "Avg time per iteration: %ld usec\n", delta / max); 53 | 54 | uloop_timeout_set(timeout, 10000); 55 | printf("test_client_notify_cb End\n"); 56 | } 57 | 58 | static struct uloop_timeout notify_timer = { 59 | .cb = test_client_notify_cb, 60 | }; 61 | 62 | static void client_main(void) 63 | { 64 | uint32_t id; 65 | int ret; 66 | 67 | printf("client_main Start\n"); 68 | ret = ubus_add_object(ctx, &test_client_object); 69 | if (ret) { 70 | fprintf(stderr, "Failed to add_object object: %s\n", ubus_strerror(ret)); 71 | return; 72 | } 73 | 74 | if (ubus_lookup_id(ctx, "test", &id)) { 75 | fprintf(stderr, "Failed to look up test object\n"); 76 | return; 77 | } 78 | 79 | blob_buf_init(&b, 0); 80 | blobmsg_add_u32(&b, "id", test_client_object.id); 81 | ubus_invoke(ctx, id, "watch", b.head, NULL, 0, 3000); 82 | test_client_notify_cb(¬ify_timer); 83 | uloop_run(); 84 | printf("client_main End\n"); 85 | } 86 | 87 | int main(int argc, char **argv) 88 | { 89 | printf("main Start"); 90 | const char *ubus_socket = NULL; 91 | int ch; 92 | 93 | while ((ch = getopt(argc, argv, "cs:")) != -1) { 94 | switch (ch) { 95 | case 's': 96 | ubus_socket = optarg; 97 | break; 98 | default: 99 | break; 100 | } 101 | } 102 | 103 | argc -= optind; 104 | argv += optind; 105 | 106 | uloop_init(); 107 | 108 | ctx = ubus_connect(ubus_socket); 109 | if (!ctx) { 110 | fprintf(stderr, "Failed to connect to ubus\n"); 111 | return -1; 112 | } 113 | 114 | ubus_add_uloop(ctx); 115 | 116 | client_main(); 117 | 118 | ubus_free(ctx); 119 | uloop_done(); 120 | 121 | printf("main End"); 122 | return 0; 123 | } 124 | -------------------------------------------------------------------------------- /examples/server.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 Felix Fietkau 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License version 2.1 6 | * as published by the Free Software Foundation 7 | * 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU General Public License for more details. 12 | */ 13 | 14 | #include 15 | 16 | #include 17 | #include "libubus.h" 18 | 19 | static struct ubus_context *ctx; 20 | static struct ubus_subscriber test_event; 21 | static struct blob_buf b; 22 | 23 | /* 24 | Enum created to have policy ordered with names 25 | */ 26 | enum { 27 | HELLO_ID, 28 | HELLO_MSG, 29 | __HELLO_MAX 30 | }; 31 | 32 | /* 33 | Policy stuff, what elements we will return in the JSON 34 | */ 35 | static const struct blobmsg_policy hello_policy[] = { 36 | [HELLO_ID] = { .name = "id", .type = BLOBMSG_TYPE_INT32 }, 37 | [HELLO_MSG] = { .name = "msg", .type = BLOBMSG_TYPE_STRING }, 38 | }; 39 | 40 | /* 41 | The request??? 42 | */ 43 | struct hello_request { 44 | struct ubus_request_data req; 45 | struct uloop_timeout timeout; 46 | char data[]; 47 | }; 48 | 49 | /* 50 | 51 | */ 52 | static void test_hello_reply(struct uloop_timeout *t) 53 | { 54 | fprintf(stderr, "test_hello_reply Start\n"); 55 | struct hello_request *req = container_of(t, struct hello_request, timeout); 56 | 57 | blob_buf_init(&b, 0); 58 | blobmsg_add_string(&b, "message", req->data); 59 | ubus_send_reply(ctx, &req->req, b.head); 60 | ubus_complete_deferred_request(ctx, &req->req, 0); 61 | free(req); 62 | fprintf(stderr, "test_hello_reply End\n"); 63 | } 64 | 65 | /** 66 | The hello callback is this one. 67 | 68 | @param ctx - The context?? 69 | @param obj - The...?? 70 | @param req - 71 | @param method - The name of the method that wants to be called 72 | @param msg - 73 | */ 74 | static int test_hello(struct ubus_context *ctx, struct ubus_object *obj, 75 | struct ubus_request_data *req, const char *method, 76 | struct blob_attr *msg) 77 | { 78 | struct hello_request *hreq; 79 | struct blob_attr *tb[__HELLO_MAX]; 80 | const char *format = "%s received a message: %s"; 81 | const char *msgstr = "(unknown)"; 82 | 83 | fprintf(stderr, "test_hello Start\n"); 84 | blobmsg_parse(hello_policy, ARRAY_SIZE(hello_policy), tb, blob_data(msg), blob_len(msg)); 85 | 86 | if (tb[HELLO_MSG]) 87 | msgstr = blobmsg_data(tb[HELLO_MSG]); 88 | 89 | hreq = calloc(1, sizeof(*hreq) + strlen(format) + strlen(obj->name) + strlen(msgstr) + 1); 90 | sprintf(hreq->data, format, obj->name, msgstr); 91 | ubus_defer_request(ctx, req, &hreq->req); 92 | hreq->timeout.cb = test_hello_reply; 93 | uloop_timeout_set(&hreq->timeout, 1000); 94 | 95 | fprintf(stderr, "test_hello End\n"); 96 | return 0; 97 | } 98 | 99 | enum { 100 | WATCH_ID, 101 | WATCH_COUNTER, 102 | __WATCH_MAX 103 | }; 104 | 105 | static const struct blobmsg_policy watch_policy[__WATCH_MAX] = { 106 | [WATCH_ID] = { .name = "id", .type = BLOBMSG_TYPE_INT32 }, 107 | [WATCH_COUNTER] = { .name = "counter", .type = BLOBMSG_TYPE_INT32 }, 108 | }; 109 | 110 | static void 111 | test_handle_remove(struct ubus_context *ctx, struct ubus_subscriber *s, 112 | uint32_t id) 113 | { 114 | fprintf(stderr, "test_handle_remove Start\n"); 115 | fprintf(stderr, "Object %08x went away\n", id); 116 | fprintf(stderr, "test_handle_remove End\n"); 117 | } 118 | 119 | /* 120 | When a method is called, displays method and params 121 | */ 122 | static int 123 | test_notify(struct ubus_context *ctx, struct ubus_object *obj, 124 | struct ubus_request_data *req, const char *method, 125 | struct blob_attr *msg) 126 | { 127 | #if 0 128 | char *str; 129 | 130 | str = blobmsg_format_json(msg, true); 131 | fprintf(stderr, "Received notification '%s': %s\n", method, str); 132 | free(str); 133 | #endif 134 | fprintf(stderr, "test_notify Start\n"); 135 | fprintf(stderr, "test_notify End\n"); 136 | return 0; 137 | } 138 | 139 | static int test_watch(struct ubus_context *ctx, struct ubus_object *obj, 140 | struct ubus_request_data *req, const char *method, 141 | struct blob_attr *msg) 142 | { 143 | fprintf(stderr, "test_watch Start\n"); 144 | struct blob_attr *tb[__WATCH_MAX]; 145 | int ret; 146 | 147 | blobmsg_parse(watch_policy, __WATCH_MAX, tb, blob_data(msg), blob_len(msg)); 148 | if (!tb[WATCH_ID]) 149 | return UBUS_STATUS_INVALID_ARGUMENT; 150 | 151 | test_event.remove_cb = test_handle_remove; // Action on remove 152 | test_event.cb = test_notify; // Say which is the objective for calls 153 | ret = ubus_subscribe(ctx, &test_event, blobmsg_get_u32(tb[WATCH_ID])); 154 | fprintf(stderr, "Watching object %08x: %s\n", blobmsg_get_u32(tb[WATCH_ID]), ubus_strerror(ret)); 155 | fprintf(stderr, "test_watch End\n"); 156 | return ret; 157 | } 158 | 159 | static const struct ubus_method test_methods[] = { 160 | UBUS_METHOD("hello", test_hello, hello_policy), 161 | UBUS_METHOD("watch", test_watch, watch_policy), 162 | }; 163 | 164 | static struct ubus_object_type test_object_type = 165 | UBUS_OBJECT_TYPE("test", test_methods); 166 | 167 | static struct ubus_object test_object = { 168 | .name = "test", 169 | .type = &test_object_type, 170 | .methods = test_methods, 171 | .n_methods = ARRAY_SIZE(test_methods), 172 | }; 173 | 174 | static void server_main(void) 175 | { 176 | fprintf(stderr, "server_main Start\n"); 177 | int ret; 178 | 179 | ret = ubus_add_object(ctx, &test_object); 180 | if (ret) 181 | fprintf(stderr, "Failed to add object: %s\n", ubus_strerror(ret)); 182 | 183 | ret = ubus_register_subscriber(ctx, &test_event); 184 | if (ret) 185 | fprintf(stderr, "Failed to add watch handler: %s\n", ubus_strerror(ret)); 186 | 187 | uloop_run(); 188 | fprintf(stderr, "server_main End\n"); 189 | } 190 | 191 | int main(int argc, char **argv) 192 | { 193 | const char *ubus_socket = NULL; 194 | int ch; 195 | 196 | while ((ch = getopt(argc, argv, "cs:")) != -1) { 197 | switch (ch) { 198 | case 's': 199 | ubus_socket = optarg; 200 | break; 201 | default: 202 | break; 203 | } 204 | } 205 | 206 | argc -= optind; 207 | argv += optind; 208 | 209 | uloop_init(); 210 | 211 | ctx = ubus_connect(ubus_socket); 212 | if (!ctx) { 213 | fprintf(stderr, "Failed to connect to ubus\n"); 214 | return -1; 215 | } 216 | 217 | ubus_add_uloop(ctx); 218 | 219 | server_main(); 220 | 221 | ubus_free(ctx); 222 | uloop_done(); 223 | 224 | return 0; 225 | } 226 | -------------------------------------------------------------------------------- /libubus-internal.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011-2012 Felix Fietkau 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License version 2.1 6 | * as published by the Free Software Foundation 7 | * 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU General Public License for more details. 12 | */ 13 | 14 | #ifndef __LIBUBUS_IO_H 15 | #define __LIBUBUS_IO_H 16 | 17 | #define __hidden __attribute__((visibility ("hidden"))) 18 | 19 | extern struct blob_buf b; 20 | extern const struct ubus_method watch_method; 21 | 22 | struct blob_attr **ubus_parse_msg(struct blob_attr *msg); 23 | void ubus_handle_data(struct uloop_fd *u, unsigned int events); 24 | int ubus_send_msg(struct ubus_context *ctx, uint32_t seq, 25 | struct blob_attr *msg, int cmd, uint32_t peer); 26 | void ubus_process_msg(struct ubus_context *ctx, struct ubus_msghdr *hdr); 27 | int __hidden ubus_start_request(struct ubus_context *ctx, struct ubus_request *req, 28 | struct blob_attr *msg, int cmd, uint32_t peer); 29 | void ubus_process_obj_msg(struct ubus_context*ctx, struct ubus_msghdr *hdr); 30 | void ubus_process_req_msg(struct ubus_context *ctx, struct ubus_msghdr *hdr); 31 | void ubus_process_pending_msg(struct ubus_context *ctx); 32 | 33 | #endif 34 | -------------------------------------------------------------------------------- /libubus-io.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011-2012 Felix Fietkau 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License version 2.1 6 | * as published by the Free Software Foundation 7 | * 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU General Public License for more details. 12 | */ 13 | 14 | #include 15 | #include 16 | #include 17 | 18 | #include 19 | #include 20 | #include 21 | 22 | #include 23 | #include 24 | #include 25 | 26 | #include "libubus.h" 27 | #include "libubus-internal.h" 28 | 29 | #define STATIC_IOV(_var) { .iov_base = (char *) &(_var), .iov_len = sizeof(_var) } 30 | 31 | static const struct blob_attr_info ubus_policy[UBUS_ATTR_MAX] = { 32 | [UBUS_ATTR_STATUS] = { .type = BLOB_ATTR_INT32 }, 33 | [UBUS_ATTR_OBJID] = { .type = BLOB_ATTR_INT32 }, 34 | [UBUS_ATTR_OBJPATH] = { .type = BLOB_ATTR_STRING }, 35 | [UBUS_ATTR_METHOD] = { .type = BLOB_ATTR_STRING }, 36 | [UBUS_ATTR_ACTIVE] = { .type = BLOB_ATTR_INT8 }, 37 | [UBUS_ATTR_NO_REPLY] = { .type = BLOB_ATTR_INT8 }, 38 | [UBUS_ATTR_SUBSCRIBERS] = { .type = BLOB_ATTR_NESTED }, 39 | }; 40 | 41 | static struct blob_attr *attrbuf[UBUS_ATTR_MAX]; 42 | 43 | __hidden struct blob_attr **ubus_parse_msg(struct blob_attr *msg) 44 | { 45 | blob_parse(msg, attrbuf, ubus_policy, UBUS_ATTR_MAX); 46 | return attrbuf; 47 | } 48 | 49 | static void wait_data(int fd, bool write) 50 | { 51 | struct pollfd pfd = { .fd = fd }; 52 | 53 | pfd.events = write ? POLLOUT : POLLIN; 54 | poll(&pfd, 1, 0); 55 | } 56 | 57 | static int writev_retry(int fd, struct iovec *iov, int iov_len) 58 | { 59 | int len = 0; 60 | 61 | do { 62 | int cur_len = writev(fd, iov, iov_len); 63 | if (cur_len < 0) { 64 | switch(errno) { 65 | case EAGAIN: 66 | wait_data(fd, true); 67 | break; 68 | case EINTR: 69 | break; 70 | default: 71 | return -1; 72 | } 73 | continue; 74 | } 75 | len += cur_len; 76 | while (cur_len >= iov->iov_len) { 77 | cur_len -= iov->iov_len; 78 | iov_len--; 79 | iov++; 80 | if (!iov_len) 81 | return len; 82 | } 83 | iov->iov_len -= cur_len; 84 | } while (1); 85 | 86 | /* Should never reach here */ 87 | return -1; 88 | } 89 | 90 | int __hidden ubus_send_msg(struct ubus_context *ctx, uint32_t seq, 91 | struct blob_attr *msg, int cmd, uint32_t peer) 92 | { 93 | struct ubus_msghdr hdr; 94 | struct iovec iov[2] = { 95 | STATIC_IOV(hdr) 96 | }; 97 | int ret; 98 | 99 | hdr.version = 0; 100 | hdr.type = cmd; 101 | hdr.seq = seq; 102 | hdr.peer = peer; 103 | 104 | if (!msg) { 105 | blob_buf_init(&b, 0); 106 | msg = b.head; 107 | } 108 | 109 | iov[1].iov_base = (char *) msg; 110 | iov[1].iov_len = blob_raw_len(msg); 111 | 112 | ret = writev_retry(ctx->sock.fd, iov, ARRAY_SIZE(iov)); 113 | if (ret < 0) 114 | ctx->sock.eof = true; 115 | 116 | return ret; 117 | } 118 | 119 | static int recv_retry(int fd, struct iovec *iov, bool wait) 120 | { 121 | int bytes, total = 0; 122 | 123 | while (iov->iov_len > 0) { 124 | if (wait) 125 | wait_data(fd, false); 126 | 127 | bytes = read(fd, iov->iov_base, iov->iov_len); 128 | if (!bytes) 129 | return -1; 130 | 131 | if (bytes < 0) { 132 | bytes = 0; 133 | if (uloop_cancelled) 134 | return 0; 135 | if (errno == EINTR) 136 | continue; 137 | 138 | if (errno != EAGAIN) 139 | return -1; 140 | } 141 | if (!wait && !bytes) 142 | return 0; 143 | 144 | wait = true; 145 | iov->iov_len -= bytes; 146 | iov->iov_base += bytes; 147 | total += bytes; 148 | } 149 | 150 | return total; 151 | } 152 | 153 | static bool ubus_validate_hdr(struct ubus_msghdr *hdr) 154 | { 155 | struct blob_attr *data = ubus_msghdr_data(hdr); 156 | 157 | if (hdr->version != 0) 158 | return false; 159 | 160 | if (blob_raw_len(data) < sizeof(*data)) 161 | return false; 162 | 163 | if (blob_pad_len(data) > UBUS_MAX_MSGLEN) 164 | return false; 165 | 166 | return true; 167 | } 168 | 169 | static bool get_next_msg(struct ubus_context *ctx) 170 | { 171 | struct iovec iov = STATIC_IOV(ctx->msgbuf.hdr); 172 | int r; 173 | 174 | /* receive header + start attribute */ 175 | iov.iov_len += sizeof(struct blob_attr); 176 | r = recv_retry(ctx->sock.fd, &iov, false); 177 | if (r <= 0) { 178 | if (r < 0) 179 | ctx->sock.eof = true; 180 | 181 | return false; 182 | } 183 | 184 | iov.iov_len = blob_len(ubus_msghdr_data(&ctx->msgbuf.hdr)); 185 | if (iov.iov_len > 0 && !recv_retry(ctx->sock.fd, &iov, true)) 186 | return false; 187 | 188 | return ubus_validate_hdr(&ctx->msgbuf.hdr); 189 | } 190 | 191 | void __hidden ubus_handle_data(struct uloop_fd *u, unsigned int events) 192 | { 193 | struct ubus_context *ctx = container_of(u, struct ubus_context, sock); 194 | struct ubus_msghdr *hdr = &ctx->msgbuf.hdr; 195 | 196 | while (get_next_msg(ctx)) { 197 | ubus_process_msg(ctx, hdr); 198 | if (uloop_cancelled) 199 | break; 200 | } 201 | 202 | if (u->eof) 203 | ctx->connection_lost(ctx); 204 | } 205 | 206 | static void 207 | ubus_refresh_state(struct ubus_context *ctx) 208 | { 209 | struct ubus_object *obj, *tmp; 210 | struct ubus_object **objs; 211 | int n, i = 0; 212 | 213 | /* clear all type IDs, they need to be registered again */ 214 | avl_for_each_element(&ctx->objects, obj, avl) 215 | if (obj->type) 216 | obj->type->id = 0; 217 | 218 | /* push out all objects again */ 219 | objs = alloca(ctx->objects.count * sizeof(*objs)); 220 | avl_remove_all_elements(&ctx->objects, obj, avl, tmp) { 221 | objs[i++] = obj; 222 | obj->id = 0; 223 | } 224 | 225 | for (n = i, i = 0; i < n; i++) 226 | ubus_add_object(ctx, objs[i]); 227 | } 228 | 229 | int ubus_reconnect(struct ubus_context *ctx, const char *path) 230 | { 231 | struct { 232 | struct ubus_msghdr hdr; 233 | struct blob_attr data; 234 | } hdr; 235 | struct blob_attr *buf; 236 | int ret = UBUS_STATUS_UNKNOWN_ERROR; 237 | 238 | if (!path) 239 | path = UBUS_UNIX_SOCKET; 240 | 241 | if (ctx->sock.fd >= 0) { 242 | if (ctx->sock.registered) 243 | uloop_fd_delete(&ctx->sock); 244 | 245 | close(ctx->sock.fd); 246 | } 247 | 248 | ctx->sock.fd = usock(USOCK_UNIX, path, NULL); 249 | if (ctx->sock.fd < 0) 250 | return UBUS_STATUS_CONNECTION_FAILED; 251 | 252 | if (read(ctx->sock.fd, &hdr, sizeof(hdr)) != sizeof(hdr)) 253 | goto out_close; 254 | 255 | if (!ubus_validate_hdr(&hdr.hdr)) 256 | goto out_close; 257 | 258 | if (hdr.hdr.type != UBUS_MSG_HELLO) 259 | goto out_close; 260 | 261 | buf = calloc(1, blob_raw_len(&hdr.data)); 262 | if (!buf) 263 | goto out_close; 264 | 265 | memcpy(buf, &hdr.data, sizeof(hdr.data)); 266 | if (read(ctx->sock.fd, blob_data(buf), blob_len(buf)) != blob_len(buf)) 267 | goto out_free; 268 | 269 | ctx->local_id = hdr.hdr.peer; 270 | if (!ctx->local_id) 271 | goto out_free; 272 | 273 | ret = UBUS_STATUS_OK; 274 | fcntl(ctx->sock.fd, F_SETFL, fcntl(ctx->sock.fd, F_GETFL) | O_NONBLOCK); 275 | 276 | ubus_refresh_state(ctx); 277 | 278 | out_free: 279 | free(buf); 280 | out_close: 281 | if (ret) 282 | close(ctx->sock.fd); 283 | 284 | return ret; 285 | } 286 | -------------------------------------------------------------------------------- /libubus-obj.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011-2012 Felix Fietkau 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License version 2.1 6 | * as published by the Free Software Foundation 7 | * 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU General Public License for more details. 12 | */ 13 | 14 | #include "libubus.h" 15 | #include "libubus-internal.h" 16 | 17 | static void 18 | ubus_process_unsubscribe(struct ubus_context *ctx, struct ubus_msghdr *hdr, 19 | struct ubus_object *obj, struct blob_attr **attrbuf) 20 | { 21 | struct ubus_subscriber *s; 22 | 23 | if (!obj || !attrbuf[UBUS_ATTR_TARGET]) 24 | return; 25 | 26 | if (obj->methods != &watch_method) 27 | return; 28 | 29 | s = container_of(obj, struct ubus_subscriber, obj); 30 | if (s->remove_cb) 31 | s->remove_cb(ctx, s, blob_get_u32(attrbuf[UBUS_ATTR_TARGET])); 32 | } 33 | 34 | static void 35 | ubus_process_notify(struct ubus_context *ctx, struct ubus_msghdr *hdr, 36 | struct ubus_object *obj, struct blob_attr **attrbuf) 37 | { 38 | if (!obj || !attrbuf[UBUS_ATTR_ACTIVE]) 39 | return; 40 | 41 | obj->has_subscribers = blob_get_u8(attrbuf[UBUS_ATTR_ACTIVE]); 42 | if (obj->subscribe_cb) 43 | obj->subscribe_cb(ctx, obj); 44 | } 45 | static void 46 | ubus_process_invoke(struct ubus_context *ctx, struct ubus_msghdr *hdr, 47 | struct ubus_object *obj, struct blob_attr **attrbuf) 48 | { 49 | struct ubus_request_data req = {}; 50 | int method; 51 | int ret; 52 | bool no_reply = false; 53 | 54 | if (!obj) { 55 | ret = UBUS_STATUS_NOT_FOUND; 56 | goto send; 57 | } 58 | 59 | if (!attrbuf[UBUS_ATTR_METHOD]) { 60 | ret = UBUS_STATUS_INVALID_ARGUMENT; 61 | goto send; 62 | } 63 | 64 | if (attrbuf[UBUS_ATTR_NO_REPLY]) 65 | no_reply = blob_get_int8(attrbuf[UBUS_ATTR_NO_REPLY]); 66 | 67 | req.peer = hdr->peer; 68 | req.seq = hdr->seq; 69 | req.object = obj->id; 70 | 71 | for (method = 0; method < obj->n_methods; method++) 72 | if (!obj->methods[method].name || 73 | !strcmp(obj->methods[method].name, 74 | blob_data(attrbuf[UBUS_ATTR_METHOD]))) 75 | goto found; 76 | 77 | /* not found */ 78 | ret = UBUS_STATUS_METHOD_NOT_FOUND; 79 | goto send; 80 | 81 | found: 82 | ret = obj->methods[method].handler(ctx, obj, &req, 83 | blob_data(attrbuf[UBUS_ATTR_METHOD]), 84 | attrbuf[UBUS_ATTR_DATA]); 85 | if (req.deferred || no_reply) 86 | return; 87 | 88 | send: 89 | ubus_complete_deferred_request(ctx, &req, ret); 90 | } 91 | 92 | void __hidden ubus_process_obj_msg(struct ubus_context *ctx, struct ubus_msghdr *hdr) 93 | { 94 | void (*cb)(struct ubus_context *, struct ubus_msghdr *, 95 | struct ubus_object *, struct blob_attr **); 96 | struct blob_attr **attrbuf; 97 | struct ubus_object *obj; 98 | uint32_t objid; 99 | 100 | attrbuf = ubus_parse_msg(ubus_msghdr_data(hdr)); 101 | if (!attrbuf[UBUS_ATTR_OBJID]) 102 | return; 103 | 104 | objid = blob_get_u32(attrbuf[UBUS_ATTR_OBJID]); 105 | obj = avl_find_element(&ctx->objects, &objid, obj, avl); 106 | 107 | switch (hdr->type) { 108 | case UBUS_MSG_INVOKE: 109 | cb = ubus_process_invoke; 110 | break; 111 | case UBUS_MSG_UNSUBSCRIBE: 112 | cb = ubus_process_unsubscribe; 113 | break; 114 | case UBUS_MSG_NOTIFY: 115 | cb = ubus_process_notify; 116 | break; 117 | default: 118 | return; 119 | } 120 | cb(ctx, hdr, obj, attrbuf); 121 | } 122 | 123 | static void ubus_add_object_cb(struct ubus_request *req, int type, struct blob_attr *msg) 124 | { 125 | struct ubus_object *obj = req->priv; 126 | struct blob_attr **attrbuf = ubus_parse_msg(msg); 127 | 128 | if (!attrbuf[UBUS_ATTR_OBJID]) 129 | return; 130 | 131 | obj->id = blob_get_u32(attrbuf[UBUS_ATTR_OBJID]); 132 | 133 | if (attrbuf[UBUS_ATTR_OBJTYPE]) 134 | obj->type->id = blob_get_u32(attrbuf[UBUS_ATTR_OBJTYPE]); 135 | 136 | obj->avl.key = &obj->id; 137 | avl_insert(&req->ctx->objects, &obj->avl); 138 | } 139 | 140 | static void ubus_push_method_data(const struct ubus_method *m) 141 | { 142 | void *mtbl; 143 | int i; 144 | 145 | mtbl = blobmsg_open_table(&b, m->name); 146 | 147 | for (i = 0; i < m->n_policy; i++) 148 | blobmsg_add_u32(&b, m->policy[i].name, m->policy[i].type); 149 | 150 | blobmsg_close_table(&b, mtbl); 151 | } 152 | 153 | static bool ubus_push_object_type(const struct ubus_object_type *type) 154 | { 155 | void *s; 156 | int i; 157 | 158 | s = blob_nest_start(&b, UBUS_ATTR_SIGNATURE); 159 | 160 | for (i = 0; i < type->n_methods; i++) 161 | ubus_push_method_data(&type->methods[i]); 162 | 163 | blob_nest_end(&b, s); 164 | 165 | return true; 166 | } 167 | 168 | int ubus_add_object(struct ubus_context *ctx, struct ubus_object *obj) 169 | { 170 | struct ubus_request req; 171 | int ret; 172 | 173 | blob_buf_init(&b, 0); 174 | 175 | if (obj->name && obj->type) { 176 | blob_put_string(&b, UBUS_ATTR_OBJPATH, obj->name); 177 | 178 | if (obj->type->id) 179 | blob_put_int32(&b, UBUS_ATTR_OBJTYPE, obj->type->id); 180 | else if (!ubus_push_object_type(obj->type)) 181 | return UBUS_STATUS_INVALID_ARGUMENT; 182 | } 183 | 184 | if (ubus_start_request(ctx, &req, b.head, UBUS_MSG_ADD_OBJECT, 0) < 0) 185 | return UBUS_STATUS_INVALID_ARGUMENT; 186 | 187 | req.raw_data_cb = ubus_add_object_cb; 188 | req.priv = obj; 189 | ret = ubus_complete_request(ctx, &req, 0); 190 | if (ret) 191 | return ret; 192 | 193 | if (!obj->id) 194 | return UBUS_STATUS_NO_DATA; 195 | 196 | return 0; 197 | } 198 | 199 | static void ubus_remove_object_cb(struct ubus_request *req, int type, struct blob_attr *msg) 200 | { 201 | struct ubus_object *obj = req->priv; 202 | struct blob_attr **attrbuf = ubus_parse_msg(msg); 203 | 204 | if (!attrbuf[UBUS_ATTR_OBJID]) 205 | return; 206 | 207 | obj->id = 0; 208 | 209 | if (attrbuf[UBUS_ATTR_OBJTYPE] && obj->type) 210 | obj->type->id = 0; 211 | 212 | avl_delete(&req->ctx->objects, &obj->avl); 213 | } 214 | 215 | int ubus_remove_object(struct ubus_context *ctx, struct ubus_object *obj) 216 | { 217 | struct ubus_request req; 218 | int ret; 219 | 220 | blob_buf_init(&b, 0); 221 | blob_put_int32(&b, UBUS_ATTR_OBJID, obj->id); 222 | 223 | if (ubus_start_request(ctx, &req, b.head, UBUS_MSG_REMOVE_OBJECT, 0) < 0) 224 | return UBUS_STATUS_INVALID_ARGUMENT; 225 | 226 | req.raw_data_cb = ubus_remove_object_cb; 227 | req.priv = obj; 228 | ret = ubus_complete_request(ctx, &req, 0); 229 | if (ret) 230 | return ret; 231 | 232 | if (obj->id) 233 | return UBUS_STATUS_NO_DATA; 234 | 235 | return 0; 236 | } 237 | -------------------------------------------------------------------------------- /libubus-req.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011-2012 Felix Fietkau 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License version 2.1 6 | * as published by the Free Software Foundation 7 | * 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU General Public License for more details. 12 | */ 13 | 14 | #include "libubus.h" 15 | #include "libubus-internal.h" 16 | 17 | struct ubus_pending_data { 18 | struct list_head list; 19 | int type; 20 | struct blob_attr data[]; 21 | }; 22 | 23 | static void req_data_cb(struct ubus_request *req, int type, struct blob_attr *data) 24 | { 25 | struct blob_attr **attr; 26 | 27 | if (req->raw_data_cb) 28 | req->raw_data_cb(req, type, data); 29 | 30 | if (!req->data_cb) 31 | return; 32 | 33 | attr = ubus_parse_msg(data); 34 | req->data_cb(req, type, attr[UBUS_ATTR_DATA]); 35 | } 36 | 37 | static void __ubus_process_req_data(struct ubus_request *req) 38 | { 39 | struct ubus_pending_data *data; 40 | 41 | while (!list_empty(&req->pending)) { 42 | data = list_first_entry(&req->pending, 43 | struct ubus_pending_data, list); 44 | list_del(&data->list); 45 | if (!req->cancelled) 46 | req_data_cb(req, data->type, data->data); 47 | free(data); 48 | } 49 | } 50 | 51 | int __hidden ubus_start_request(struct ubus_context *ctx, struct ubus_request *req, 52 | struct blob_attr *msg, int cmd, uint32_t peer) 53 | { 54 | memset(req, 0, sizeof(*req)); 55 | 56 | if (msg && blob_pad_len(msg) > UBUS_MAX_MSGLEN) 57 | return -1; 58 | 59 | INIT_LIST_HEAD(&req->list); 60 | INIT_LIST_HEAD(&req->pending); 61 | req->ctx = ctx; 62 | req->peer = peer; 63 | req->seq = ++ctx->request_seq; 64 | return ubus_send_msg(ctx, req->seq, msg, cmd, peer); 65 | } 66 | 67 | void ubus_abort_request(struct ubus_context *ctx, struct ubus_request *req) 68 | { 69 | if (list_empty(&req->list)) 70 | return; 71 | 72 | req->cancelled = true; 73 | __ubus_process_req_data(req); 74 | list_del_init(&req->list); 75 | } 76 | 77 | void ubus_complete_request_async(struct ubus_context *ctx, struct ubus_request *req) 78 | { 79 | if (!list_empty(&req->list)) 80 | return; 81 | 82 | list_add(&req->list, &ctx->requests); 83 | } 84 | 85 | static void 86 | ubus_req_complete_cb(struct ubus_request *req) 87 | { 88 | ubus_complete_handler_t cb = req->complete_cb; 89 | 90 | if (!cb) 91 | return; 92 | 93 | req->complete_cb = NULL; 94 | cb(req, req->status_code); 95 | } 96 | 97 | static void 98 | ubus_set_req_status(struct ubus_request *req, int ret) 99 | { 100 | if (!list_empty(&req->list)) 101 | list_del_init(&req->list); 102 | 103 | req->status_msg = true; 104 | req->status_code = ret; 105 | if (!req->blocked) 106 | ubus_req_complete_cb(req); 107 | } 108 | 109 | static void ubus_sync_req_cb(struct ubus_request *req, int ret) 110 | { 111 | req->status_msg = true; 112 | req->status_code = ret; 113 | uloop_end(); 114 | } 115 | 116 | struct ubus_sync_req_cb { 117 | struct uloop_timeout timeout; 118 | struct ubus_request *req; 119 | }; 120 | 121 | static void ubus_sync_req_timeout_cb(struct uloop_timeout *timeout) 122 | { 123 | struct ubus_sync_req_cb *cb; 124 | 125 | cb = container_of(timeout, struct ubus_sync_req_cb, timeout); 126 | ubus_set_req_status(cb->req, UBUS_STATUS_TIMEOUT); 127 | } 128 | 129 | int ubus_complete_request(struct ubus_context *ctx, struct ubus_request *req, 130 | int timeout) 131 | { 132 | struct ubus_sync_req_cb cb; 133 | ubus_complete_handler_t complete_cb = req->complete_cb; 134 | bool registered = ctx->sock.registered; 135 | int status = UBUS_STATUS_NO_DATA; 136 | 137 | if (!registered) { 138 | uloop_init(); 139 | ubus_add_uloop(ctx); 140 | } 141 | 142 | if (timeout) { 143 | memset(&cb, 0, sizeof(cb)); 144 | cb.req = req; 145 | cb.timeout.cb = ubus_sync_req_timeout_cb; 146 | uloop_timeout_set(&cb.timeout, timeout); 147 | } 148 | 149 | ubus_complete_request_async(ctx, req); 150 | req->complete_cb = ubus_sync_req_cb; 151 | 152 | ctx->stack_depth++; 153 | while (!req->status_msg) { 154 | bool cancelled = uloop_cancelled; 155 | uloop_cancelled = false; 156 | uloop_run(); 157 | uloop_cancelled = cancelled; 158 | } 159 | ctx->stack_depth--; 160 | if (ctx->stack_depth) 161 | uloop_cancelled = true; 162 | 163 | if (timeout) 164 | uloop_timeout_cancel(&cb.timeout); 165 | 166 | if (req->status_msg) 167 | status = req->status_code; 168 | 169 | req->complete_cb = complete_cb; 170 | if (req->complete_cb) 171 | req->complete_cb(req, status); 172 | 173 | if (!registered) 174 | uloop_fd_delete(&ctx->sock); 175 | 176 | if (!ctx->stack_depth) 177 | ubus_process_pending_msg(ctx); 178 | 179 | return status; 180 | } 181 | 182 | void ubus_complete_deferred_request(struct ubus_context *ctx, struct ubus_request_data *req, int ret) 183 | { 184 | blob_buf_init(&b, 0); 185 | blob_put_int32(&b, UBUS_ATTR_STATUS, ret); 186 | blob_put_int32(&b, UBUS_ATTR_OBJID, req->object); 187 | ubus_send_msg(ctx, req->seq, b.head, UBUS_MSG_STATUS, req->peer); 188 | } 189 | 190 | int ubus_send_reply(struct ubus_context *ctx, struct ubus_request_data *req, 191 | struct blob_attr *msg) 192 | { 193 | int ret; 194 | 195 | blob_buf_init(&b, 0); 196 | blob_put_int32(&b, UBUS_ATTR_OBJID, req->object); 197 | blob_put(&b, UBUS_ATTR_DATA, blob_data(msg), blob_len(msg)); 198 | ret = ubus_send_msg(ctx, req->seq, b.head, UBUS_MSG_DATA, req->peer); 199 | if (ret < 0) 200 | return UBUS_STATUS_NO_DATA; 201 | 202 | return 0; 203 | } 204 | 205 | int ubus_invoke_async(struct ubus_context *ctx, uint32_t obj, const char *method, 206 | struct blob_attr *msg, struct ubus_request *req) 207 | { 208 | blob_buf_init(&b, 0); 209 | blob_put_int32(&b, UBUS_ATTR_OBJID, obj); 210 | blob_put_string(&b, UBUS_ATTR_METHOD, method); 211 | if (msg) 212 | blob_put(&b, UBUS_ATTR_DATA, blob_data(msg), blob_len(msg)); 213 | 214 | if (ubus_start_request(ctx, req, b.head, UBUS_MSG_INVOKE, obj) < 0) 215 | return UBUS_STATUS_INVALID_ARGUMENT; 216 | 217 | return 0; 218 | } 219 | 220 | int ubus_invoke(struct ubus_context *ctx, uint32_t obj, const char *method, 221 | struct blob_attr *msg, ubus_data_handler_t cb, void *priv, 222 | int timeout) 223 | { 224 | struct ubus_request req; 225 | 226 | ubus_invoke_async(ctx, obj, method, msg, &req); 227 | req.data_cb = cb; 228 | req.priv = priv; 229 | return ubus_complete_request(ctx, &req, timeout); 230 | } 231 | 232 | static void 233 | ubus_notify_complete_cb(struct ubus_request *req, int ret) 234 | { 235 | struct ubus_notify_request *nreq; 236 | 237 | nreq = container_of(req, struct ubus_notify_request, req); 238 | if (!nreq->complete_cb) 239 | return; 240 | 241 | nreq->complete_cb(nreq, 0, 0); 242 | } 243 | 244 | static int 245 | __ubus_notify_async(struct ubus_context *ctx, struct ubus_object *obj, 246 | const char *type, struct blob_attr *msg, 247 | struct ubus_notify_request *req, bool reply) 248 | { 249 | memset(req, 0, sizeof(*req)); 250 | 251 | blob_buf_init(&b, 0); 252 | blob_put_int32(&b, UBUS_ATTR_OBJID, obj->id); 253 | blob_put_string(&b, UBUS_ATTR_METHOD, type); 254 | 255 | if (!reply) 256 | blob_put_int8(&b, UBUS_ATTR_NO_REPLY, true); 257 | 258 | if (msg) 259 | blob_put(&b, UBUS_ATTR_DATA, blob_data(msg), blob_len(msg)); 260 | 261 | if (ubus_start_request(ctx, &req->req, b.head, UBUS_MSG_NOTIFY, obj->id) < 0) 262 | return UBUS_STATUS_INVALID_ARGUMENT; 263 | 264 | /* wait for status message from ubusd first */ 265 | req->req.notify = true; 266 | req->pending = 1; 267 | req->id[0] = obj->id; 268 | req->req.complete_cb = ubus_notify_complete_cb; 269 | 270 | return 0; 271 | } 272 | 273 | int ubus_notify_async(struct ubus_context *ctx, struct ubus_object *obj, 274 | const char *type, struct blob_attr *msg, 275 | struct ubus_notify_request *req) 276 | { 277 | return __ubus_notify_async(ctx, obj, type, msg, req, true); 278 | } 279 | 280 | int ubus_notify(struct ubus_context *ctx, struct ubus_object *obj, 281 | const char *type, struct blob_attr *msg, int timeout) 282 | { 283 | struct ubus_notify_request req; 284 | int ret; 285 | 286 | ret = __ubus_notify_async(ctx, obj, type, msg, &req, timeout >= 0); 287 | if (ret < 0) 288 | return ret; 289 | 290 | if (timeout < 0) { 291 | ubus_abort_request(ctx, &req.req); 292 | return 0; 293 | } 294 | 295 | return ubus_complete_request(ctx, &req.req, timeout); 296 | } 297 | 298 | static bool ubus_get_status(struct ubus_msghdr *hdr, int *ret) 299 | { 300 | struct blob_attr **attrbuf = ubus_parse_msg(ubus_msghdr_data(hdr)); 301 | 302 | if (!attrbuf[UBUS_ATTR_STATUS]) 303 | return false; 304 | 305 | *ret = blob_get_u32(attrbuf[UBUS_ATTR_STATUS]); 306 | return true; 307 | } 308 | 309 | static int 310 | ubus_process_req_status(struct ubus_request *req, struct ubus_msghdr *hdr) 311 | { 312 | int ret = UBUS_STATUS_INVALID_ARGUMENT; 313 | 314 | ubus_get_status(hdr, &ret); 315 | req->peer = hdr->peer; 316 | ubus_set_req_status(req, ret); 317 | 318 | return ret; 319 | } 320 | 321 | static void 322 | ubus_process_req_data(struct ubus_request *req, struct ubus_msghdr *hdr) 323 | { 324 | struct blob_attr *msg_data = ubus_msghdr_data(hdr); 325 | struct ubus_pending_data *data; 326 | int len; 327 | 328 | if (!req->blocked) { 329 | req->blocked = true; 330 | req_data_cb(req, hdr->type, msg_data); 331 | __ubus_process_req_data(req); 332 | req->blocked = false; 333 | 334 | if (req->status_msg) 335 | ubus_req_complete_cb(req); 336 | 337 | return; 338 | } 339 | 340 | len = blob_raw_len(msg_data); 341 | data = calloc(1, sizeof(*data) + len); 342 | if (!data) 343 | return; 344 | 345 | data->type = hdr->type; 346 | memcpy(data->data, msg_data, len); 347 | list_add(&data->list, &req->pending); 348 | } 349 | 350 | static int 351 | ubus_find_notify_id(struct ubus_notify_request *n, uint32_t objid) 352 | { 353 | uint32_t pending = n->pending; 354 | int i; 355 | 356 | for (i = 0; pending; i++, pending >>= 1) { 357 | if (!(pending & 1)) 358 | continue; 359 | 360 | if (n->id[i] == objid) 361 | return i; 362 | } 363 | 364 | return -1; 365 | } 366 | 367 | static struct ubus_request * 368 | ubus_find_request(struct ubus_context *ctx, uint32_t seq, uint32_t peer, int *id) 369 | { 370 | struct ubus_request *req; 371 | 372 | list_for_each_entry(req, &ctx->requests, list) { 373 | struct ubus_notify_request *nreq; 374 | nreq = container_of(req, struct ubus_notify_request, req); 375 | 376 | if (seq != req->seq) 377 | continue; 378 | 379 | if (req->notify) { 380 | if (!nreq->pending) 381 | continue; 382 | 383 | *id = ubus_find_notify_id(nreq, peer); 384 | if (*id < 0) 385 | continue; 386 | } else if (peer != req->peer) 387 | continue; 388 | 389 | return req; 390 | } 391 | return NULL; 392 | } 393 | 394 | static void ubus_process_notify_status(struct ubus_request *req, int id, struct ubus_msghdr *hdr) 395 | { 396 | struct ubus_notify_request *nreq; 397 | struct blob_attr **tb; 398 | struct blob_attr *cur; 399 | int rem, idx = 1; 400 | int ret = 0; 401 | 402 | nreq = container_of(req, struct ubus_notify_request, req); 403 | nreq->pending &= ~(1 << id); 404 | 405 | if (!id) { 406 | /* first id: ubusd's status message with a list of ids */ 407 | tb = ubus_parse_msg(ubus_msghdr_data(hdr)); 408 | if (tb[UBUS_ATTR_SUBSCRIBERS]) { 409 | blob_for_each_attr(cur, tb[UBUS_ATTR_SUBSCRIBERS], rem) { 410 | if (!blob_check_type(blob_data(cur), blob_len(cur), BLOB_ATTR_INT32)) 411 | continue; 412 | 413 | nreq->pending |= (1 << idx); 414 | nreq->id[idx] = blob_get_int32(cur); 415 | idx++; 416 | 417 | if (idx == UBUS_MAX_NOTIFY_PEERS + 1) 418 | break; 419 | } 420 | } 421 | } else { 422 | ubus_get_status(hdr, &ret); 423 | if (nreq->status_cb) 424 | nreq->status_cb(nreq, id, ret); 425 | } 426 | 427 | if (!nreq->pending) 428 | ubus_set_req_status(req, 0); 429 | } 430 | 431 | void __hidden ubus_process_req_msg(struct ubus_context *ctx, struct ubus_msghdr *hdr) 432 | { 433 | struct ubus_request *req; 434 | int id = -1; 435 | 436 | switch(hdr->type) { 437 | case UBUS_MSG_STATUS: 438 | req = ubus_find_request(ctx, hdr->seq, hdr->peer, &id); 439 | if (!req) 440 | break; 441 | 442 | if (id >= 0) 443 | ubus_process_notify_status(req, id, hdr); 444 | else 445 | ubus_process_req_status(req, hdr); 446 | break; 447 | 448 | case UBUS_MSG_DATA: 449 | req = ubus_find_request(ctx, hdr->seq, hdr->peer, &id); 450 | if (req && (req->data_cb || req->raw_data_cb)) 451 | ubus_process_req_data(req, hdr); 452 | break; 453 | } 454 | } 455 | -------------------------------------------------------------------------------- /libubus-sub.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011-2012 Felix Fietkau 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License version 2.1 6 | * as published by the Free Software Foundation 7 | * 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU General Public License for more details. 12 | */ 13 | 14 | #include "libubus.h" 15 | #include "libubus-internal.h" 16 | 17 | static int ubus_subscriber_cb(struct ubus_context *ctx, struct ubus_object *obj, 18 | struct ubus_request_data *req, 19 | const char *method, struct blob_attr *msg) 20 | { 21 | struct ubus_subscriber *s; 22 | 23 | s = container_of(obj, struct ubus_subscriber, obj); 24 | if (s->cb) 25 | return s->cb(ctx, obj, req, method, msg); 26 | return 0; 27 | } 28 | 29 | const struct ubus_method watch_method __hidden = { 30 | .name = NULL, 31 | .handler = ubus_subscriber_cb, 32 | }; 33 | 34 | int ubus_register_subscriber(struct ubus_context *ctx, struct ubus_subscriber *s) 35 | { 36 | struct ubus_object *obj = &s->obj; 37 | 38 | obj->methods = &watch_method; 39 | obj->n_methods = 1; 40 | 41 | return ubus_add_object(ctx, obj); 42 | } 43 | 44 | static int 45 | __ubus_subscribe_request(struct ubus_context *ctx, struct ubus_object *obj, uint32_t id, int type) 46 | { 47 | struct ubus_request req; 48 | 49 | blob_buf_init(&b, 0); 50 | blob_put_int32(&b, UBUS_ATTR_OBJID, obj->id); 51 | blob_put_int32(&b, UBUS_ATTR_TARGET, id); 52 | 53 | if (ubus_start_request(ctx, &req, b.head, type, 0) < 0) 54 | return UBUS_STATUS_INVALID_ARGUMENT; 55 | 56 | return ubus_complete_request(ctx, &req, 0); 57 | 58 | } 59 | 60 | int ubus_subscribe(struct ubus_context *ctx, struct ubus_subscriber *obj, uint32_t id) 61 | { 62 | return __ubus_subscribe_request(ctx, &obj->obj, id, UBUS_MSG_SUBSCRIBE); 63 | } 64 | 65 | int ubus_unsubscribe(struct ubus_context *ctx, struct ubus_subscriber *obj, uint32_t id) 66 | { 67 | return __ubus_subscribe_request(ctx, &obj->obj, id, UBUS_MSG_UNSUBSCRIBE); 68 | } 69 | 70 | -------------------------------------------------------------------------------- /libubus.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011-2012 Felix Fietkau 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License version 2.1 6 | * as published by the Free Software Foundation 7 | * 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU General Public License for more details. 12 | */ 13 | 14 | #include 15 | #include 16 | #include 17 | 18 | #include 19 | #include 20 | 21 | #include "libubus.h" 22 | #include "libubus-internal.h" 23 | #include "ubusmsg.h" 24 | 25 | const char *__ubus_strerror[__UBUS_STATUS_LAST] = { 26 | [UBUS_STATUS_OK] = "Success", 27 | [UBUS_STATUS_INVALID_COMMAND] = "Invalid command", 28 | [UBUS_STATUS_INVALID_ARGUMENT] = "Invalid argument", 29 | [UBUS_STATUS_METHOD_NOT_FOUND] = "Method not found", 30 | [UBUS_STATUS_NOT_FOUND] = "Not found", 31 | [UBUS_STATUS_NO_DATA] = "No response", 32 | [UBUS_STATUS_PERMISSION_DENIED] = "Permission denied", 33 | [UBUS_STATUS_TIMEOUT] = "Request timed out", 34 | [UBUS_STATUS_NOT_SUPPORTED] = "Operation not supported", 35 | [UBUS_STATUS_UNKNOWN_ERROR] = "Unknown error", 36 | [UBUS_STATUS_CONNECTION_FAILED] = "Connection failed", 37 | }; 38 | 39 | struct blob_buf b __hidden = {}; 40 | 41 | struct ubus_pending_msg { 42 | struct list_head list; 43 | struct ubus_msghdr hdr; 44 | }; 45 | 46 | static int ubus_cmp_id(const void *k1, const void *k2, void *ptr) 47 | { 48 | const uint32_t *id1 = k1, *id2 = k2; 49 | 50 | if (*id1 < *id2) 51 | return -1; 52 | else 53 | return *id1 > *id2; 54 | } 55 | 56 | const char *ubus_strerror(int error) 57 | { 58 | static char err[32]; 59 | 60 | if (error < 0 || error >= __UBUS_STATUS_LAST) 61 | goto out; 62 | 63 | if (!__ubus_strerror[error]) 64 | goto out; 65 | 66 | return __ubus_strerror[error]; 67 | 68 | out: 69 | sprintf(err, "Unknown error: %d", error); 70 | return err; 71 | } 72 | 73 | static void 74 | ubus_queue_msg(struct ubus_context *ctx, struct ubus_msghdr *hdr) 75 | { 76 | struct ubus_pending_msg *pending; 77 | 78 | pending = calloc(1, sizeof(*pending) + blob_raw_len(ubus_msghdr_data(hdr))); 79 | if (!pending) 80 | return; 81 | 82 | memcpy(&pending->hdr, hdr, sizeof(*hdr) + blob_raw_len(ubus_msghdr_data(hdr))); 83 | list_add(&pending->list, &ctx->pending); 84 | } 85 | 86 | void __hidden 87 | ubus_process_msg(struct ubus_context *ctx, struct ubus_msghdr *hdr) 88 | { 89 | 90 | switch(hdr->type) { 91 | case UBUS_MSG_STATUS: 92 | case UBUS_MSG_DATA: 93 | ubus_process_req_msg(ctx, hdr); 94 | break; 95 | 96 | case UBUS_MSG_INVOKE: 97 | case UBUS_MSG_UNSUBSCRIBE: 98 | case UBUS_MSG_NOTIFY: 99 | if (ctx->stack_depth > 2) { 100 | ubus_queue_msg(ctx, hdr); 101 | break; 102 | } 103 | 104 | ubus_process_obj_msg(ctx, hdr); 105 | break; 106 | } 107 | } 108 | 109 | void __hidden ubus_process_pending_msg(struct ubus_context *ctx) 110 | { 111 | struct ubus_pending_msg *pending; 112 | 113 | while (!list_empty(&ctx->pending)) { 114 | pending = list_first_entry(&ctx->pending, struct ubus_pending_msg, list); 115 | list_del(&pending->list); 116 | ubus_process_msg(ctx, &pending->hdr); 117 | free(pending); 118 | if (ctx->stack_depth > 2) 119 | break; 120 | } 121 | } 122 | 123 | struct ubus_lookup_request { 124 | struct ubus_request req; 125 | ubus_lookup_handler_t cb; 126 | }; 127 | 128 | static void ubus_lookup_cb(struct ubus_request *ureq, int type, struct blob_attr *msg) 129 | { 130 | struct ubus_lookup_request *req; 131 | struct ubus_object_data obj; 132 | struct blob_attr **attr; 133 | 134 | req = container_of(ureq, struct ubus_lookup_request, req); 135 | attr = ubus_parse_msg(msg); 136 | 137 | if (!attr[UBUS_ATTR_OBJID] || !attr[UBUS_ATTR_OBJPATH] || 138 | !attr[UBUS_ATTR_OBJTYPE]) 139 | return; 140 | 141 | memset(&obj, 0, sizeof(obj)); 142 | obj.id = blob_get_u32(attr[UBUS_ATTR_OBJID]); 143 | obj.path = blob_data(attr[UBUS_ATTR_OBJPATH]); 144 | obj.type_id = blob_get_u32(attr[UBUS_ATTR_OBJTYPE]); 145 | obj.signature = attr[UBUS_ATTR_SIGNATURE]; 146 | req->cb(ureq->ctx, &obj, ureq->priv); 147 | } 148 | 149 | int ubus_lookup(struct ubus_context *ctx, const char *path, 150 | ubus_lookup_handler_t cb, void *priv) 151 | { 152 | struct ubus_lookup_request lookup; 153 | 154 | blob_buf_init(&b, 0); 155 | if (path) 156 | blob_put_string(&b, UBUS_ATTR_OBJPATH, path); 157 | 158 | if (ubus_start_request(ctx, &lookup.req, b.head, UBUS_MSG_LOOKUP, 0) < 0) 159 | return UBUS_STATUS_INVALID_ARGUMENT; 160 | 161 | lookup.req.raw_data_cb = ubus_lookup_cb; 162 | lookup.req.priv = priv; 163 | lookup.cb = cb; 164 | return ubus_complete_request(ctx, &lookup.req, 0); 165 | } 166 | 167 | static void ubus_lookup_id_cb(struct ubus_request *req, int type, struct blob_attr *msg) 168 | { 169 | struct blob_attr **attr; 170 | uint32_t *id = req->priv; 171 | 172 | attr = ubus_parse_msg(msg); 173 | 174 | if (!attr[UBUS_ATTR_OBJID]) 175 | return; 176 | 177 | *id = blob_get_u32(attr[UBUS_ATTR_OBJID]); 178 | } 179 | 180 | int ubus_lookup_id(struct ubus_context *ctx, const char *path, uint32_t *id) 181 | { 182 | struct ubus_request req; 183 | 184 | blob_buf_init(&b, 0); 185 | if (path) 186 | blob_put_string(&b, UBUS_ATTR_OBJPATH, path); 187 | 188 | if (ubus_start_request(ctx, &req, b.head, UBUS_MSG_LOOKUP, 0) < 0) 189 | return UBUS_STATUS_INVALID_ARGUMENT; 190 | 191 | req.raw_data_cb = ubus_lookup_id_cb; 192 | req.priv = id; 193 | 194 | return ubus_complete_request(ctx, &req, 0); 195 | } 196 | 197 | static int ubus_event_cb(struct ubus_context *ctx, struct ubus_object *obj, 198 | struct ubus_request_data *req, 199 | const char *method, struct blob_attr *msg) 200 | { 201 | struct ubus_event_handler *ev; 202 | 203 | ev = container_of(obj, struct ubus_event_handler, obj); 204 | ev->cb(ctx, ev, method, msg); 205 | return 0; 206 | } 207 | 208 | static const struct ubus_method event_method = { 209 | .name = NULL, 210 | .handler = ubus_event_cb, 211 | }; 212 | 213 | int ubus_register_event_handler(struct ubus_context *ctx, 214 | struct ubus_event_handler *ev, 215 | const char *pattern) 216 | { 217 | struct ubus_object *obj = &ev->obj; 218 | struct blob_buf b2; 219 | int ret; 220 | 221 | if (!obj->id) { 222 | obj->methods = &event_method; 223 | obj->n_methods = 1; 224 | 225 | if (!!obj->name ^ !!obj->type) 226 | return UBUS_STATUS_INVALID_ARGUMENT; 227 | 228 | ret = ubus_add_object(ctx, obj); 229 | if (ret) 230 | return ret; 231 | } 232 | 233 | /* use a second buffer, ubus_invoke() overwrites the primary one */ 234 | memset(&b2, 0, sizeof(b2)); 235 | blob_buf_init(&b2, 0); 236 | blobmsg_add_u32(&b2, "object", obj->id); 237 | if (pattern) 238 | blobmsg_add_string(&b2, "pattern", pattern); 239 | 240 | return ubus_invoke(ctx, UBUS_SYSTEM_OBJECT_EVENT, "register", b2.head, 241 | NULL, NULL, 0); 242 | } 243 | 244 | int ubus_send_event(struct ubus_context *ctx, const char *id, 245 | struct blob_attr *data) 246 | { 247 | struct ubus_request req; 248 | void *s; 249 | 250 | blob_buf_init(&b, 0); 251 | blob_put_int32(&b, UBUS_ATTR_OBJID, UBUS_SYSTEM_OBJECT_EVENT); 252 | blob_put_string(&b, UBUS_ATTR_METHOD, "send"); 253 | s = blob_nest_start(&b, UBUS_ATTR_DATA); 254 | blobmsg_add_string(&b, "id", id); 255 | blobmsg_add_field(&b, BLOBMSG_TYPE_TABLE, "data", blob_data(data), blob_len(data)); 256 | blob_nest_end(&b, s); 257 | 258 | if (ubus_start_request(ctx, &req, b.head, UBUS_MSG_INVOKE, UBUS_SYSTEM_OBJECT_EVENT) < 0) 259 | return UBUS_STATUS_INVALID_ARGUMENT; 260 | 261 | return ubus_complete_request(ctx, &req, 0); 262 | } 263 | 264 | static void ubus_default_connection_lost(struct ubus_context *ctx) 265 | { 266 | if (ctx->sock.registered) 267 | uloop_end(); 268 | } 269 | 270 | struct ubus_context *ubus_connect(const char *path) 271 | { 272 | struct ubus_context *ctx; 273 | 274 | ctx = calloc(1, sizeof(*ctx)); 275 | if (!ctx) 276 | return NULL; 277 | 278 | ctx->sock.fd = -1; 279 | ctx->sock.cb = ubus_handle_data; 280 | ctx->connection_lost = ubus_default_connection_lost; 281 | 282 | INIT_LIST_HEAD(&ctx->requests); 283 | INIT_LIST_HEAD(&ctx->pending); 284 | avl_init(&ctx->objects, ubus_cmp_id, false, NULL); 285 | if (ubus_reconnect(ctx, path)) { 286 | free(ctx); 287 | ctx = NULL; 288 | } 289 | 290 | return ctx; 291 | } 292 | 293 | void ubus_free(struct ubus_context *ctx) 294 | { 295 | blob_buf_free(&b); 296 | close(ctx->sock.fd); 297 | free(ctx); 298 | } 299 | -------------------------------------------------------------------------------- /libubus.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011-2012 Felix Fietkau 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License version 2.1 6 | * as published by the Free Software Foundation 7 | * 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU General Public License for more details. 12 | */ 13 | 14 | #ifndef __LIBUBUS_H 15 | #define __LIBUBUS_H 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include "ubusmsg.h" 23 | #include "ubus_common.h" 24 | 25 | #define UBUS_MAX_NOTIFY_PEERS 16 26 | 27 | struct ubus_context; 28 | struct ubus_msg_src; 29 | struct ubus_object; 30 | struct ubus_request; 31 | struct ubus_request_data; 32 | struct ubus_object_data; 33 | struct ubus_event_handler; 34 | struct ubus_subscriber; 35 | struct ubus_notify_request; 36 | 37 | static inline struct blob_attr * 38 | ubus_msghdr_data(struct ubus_msghdr *hdr) 39 | { 40 | return (struct blob_attr *) (hdr + 1); 41 | } 42 | 43 | typedef void (*ubus_lookup_handler_t)(struct ubus_context *ctx, 44 | struct ubus_object_data *obj, 45 | void *priv); 46 | typedef int (*ubus_handler_t)(struct ubus_context *ctx, struct ubus_object *obj, 47 | struct ubus_request_data *req, 48 | const char *method, struct blob_attr *msg); 49 | typedef void (*ubus_state_handler_t)(struct ubus_context *ctx, struct ubus_object *obj); 50 | typedef void (*ubus_remove_handler_t)(struct ubus_context *ctx, 51 | struct ubus_subscriber *obj, uint32_t id); 52 | typedef void (*ubus_event_handler_t)(struct ubus_context *ctx, struct ubus_event_handler *ev, 53 | const char *type, struct blob_attr *msg); 54 | typedef void (*ubus_data_handler_t)(struct ubus_request *req, 55 | int type, struct blob_attr *msg); 56 | typedef void (*ubus_complete_handler_t)(struct ubus_request *req, int ret); 57 | typedef void (*ubus_notify_complete_handler_t)(struct ubus_notify_request *req, 58 | int idx, int ret); 59 | 60 | #define UBUS_OBJECT_TYPE(_name, _methods) \ 61 | { \ 62 | .name = _name, \ 63 | .id = 0, \ 64 | .n_methods = ARRAY_SIZE(_methods), \ 65 | .methods = _methods \ 66 | } 67 | 68 | #define UBUS_METHOD(_name, _handler, _policy) \ 69 | { \ 70 | .name = _name, \ 71 | .handler = _handler, \ 72 | .policy = _policy, \ 73 | .n_policy = ARRAY_SIZE(_policy) \ 74 | } 75 | 76 | #define UBUS_METHOD_NOARG(_name, _handler) \ 77 | { \ 78 | .name = _name, \ 79 | .handler = _handler, \ 80 | } 81 | 82 | /* Struct to represent a method to be called*/ 83 | struct ubus_method { 84 | const char *name; 85 | ubus_handler_t handler; 86 | 87 | const struct blobmsg_policy *policy; 88 | int n_policy; 89 | }; 90 | 91 | struct ubus_object_type { 92 | const char *name; 93 | uint32_t id; 94 | 95 | const struct ubus_method *methods; 96 | int n_methods; 97 | }; 98 | 99 | struct ubus_object { 100 | struct avl_node avl; 101 | 102 | const char *name; 103 | uint32_t id; 104 | 105 | const char *path; 106 | struct ubus_object_type *type; 107 | 108 | ubus_state_handler_t subscribe_cb; 109 | bool has_subscribers; 110 | 111 | const struct ubus_method *methods; 112 | int n_methods; 113 | }; 114 | 115 | struct ubus_subscriber { 116 | struct ubus_object obj; 117 | 118 | ubus_handler_t cb; 119 | ubus_remove_handler_t remove_cb; 120 | }; 121 | 122 | struct ubus_event_handler { 123 | struct ubus_object obj; 124 | 125 | ubus_event_handler_t cb; 126 | }; 127 | 128 | struct ubus_context { 129 | struct list_head requests; 130 | struct avl_tree objects; 131 | struct list_head pending; 132 | 133 | struct uloop_fd sock; 134 | 135 | uint32_t local_id; 136 | uint16_t request_seq; 137 | int stack_depth; 138 | 139 | void (*connection_lost)(struct ubus_context *ctx); 140 | 141 | struct { 142 | struct ubus_msghdr hdr; 143 | char data[UBUS_MAX_MSGLEN]; 144 | } msgbuf; 145 | }; 146 | 147 | struct ubus_object_data { 148 | uint32_t id; 149 | uint32_t type_id; 150 | const char *path; 151 | struct blob_attr *signature; 152 | }; 153 | 154 | struct ubus_request_data { 155 | uint32_t object; 156 | uint32_t peer; 157 | uint16_t seq; 158 | bool deferred; 159 | }; 160 | 161 | struct ubus_request { 162 | struct list_head list; 163 | 164 | struct list_head pending; 165 | int status_code; 166 | bool status_msg; 167 | bool blocked; 168 | bool cancelled; 169 | bool notify; 170 | 171 | uint32_t peer; 172 | uint16_t seq; 173 | 174 | ubus_data_handler_t raw_data_cb; 175 | ubus_data_handler_t data_cb; 176 | ubus_complete_handler_t complete_cb; 177 | 178 | struct ubus_context *ctx; 179 | void *priv; 180 | }; 181 | 182 | struct ubus_notify_request { 183 | struct ubus_request req; 184 | 185 | ubus_notify_complete_handler_t status_cb; 186 | ubus_notify_complete_handler_t complete_cb; 187 | 188 | uint32_t pending; 189 | uint32_t id[UBUS_MAX_NOTIFY_PEERS + 1]; 190 | }; 191 | 192 | struct ubus_context *ubus_connect(const char *path); 193 | int ubus_reconnect(struct ubus_context *ctx, const char *path); 194 | void ubus_free(struct ubus_context *ctx); 195 | 196 | const char *ubus_strerror(int error); 197 | 198 | static inline void ubus_add_uloop(struct ubus_context *ctx) 199 | { 200 | uloop_fd_add(&ctx->sock, ULOOP_BLOCKING | ULOOP_READ); 201 | } 202 | 203 | /* call this for read events on ctx->sock.fd when not using uloop */ 204 | static inline void ubus_handle_event(struct ubus_context *ctx) 205 | { 206 | ctx->sock.cb(&ctx->sock, ULOOP_READ); 207 | } 208 | 209 | /* ----------- raw request handling ----------- */ 210 | 211 | /* wait for a request to complete and return its status */ 212 | int ubus_complete_request(struct ubus_context *ctx, struct ubus_request *req, 213 | int timeout); 214 | 215 | /* complete a request asynchronously */ 216 | void ubus_complete_request_async(struct ubus_context *ctx, 217 | struct ubus_request *req); 218 | 219 | /* abort an asynchronous request */ 220 | void ubus_abort_request(struct ubus_context *ctx, struct ubus_request *req); 221 | 222 | /* ----------- objects ----------- */ 223 | 224 | /* Lookup for a registered path inside ubus */ 225 | int ubus_lookup(struct ubus_context *ctx, const char *path, 226 | ubus_lookup_handler_t cb, void *priv); 227 | 228 | /* Search for the ID of the given path */ 229 | int ubus_lookup_id(struct ubus_context *ctx, const char *path, uint32_t *id); 230 | 231 | /* make an object visible to remote connections */ 232 | int ubus_add_object(struct ubus_context *ctx, struct ubus_object *obj); 233 | 234 | /* remove the object from the ubus connection */ 235 | int ubus_remove_object(struct ubus_context *ctx, struct ubus_object *obj); 236 | 237 | /* add a subscriber notifications from another object */ 238 | int ubus_register_subscriber(struct ubus_context *ctx, struct ubus_subscriber *obj); 239 | 240 | /* delete a notification from another object */ 241 | static inline int 242 | ubus_unregister_subscriber(struct ubus_context *ctx, struct ubus_subscriber *obj) 243 | { 244 | return ubus_remove_object(ctx, &obj->obj); 245 | } 246 | 247 | int ubus_subscribe(struct ubus_context *ctx, struct ubus_subscriber *obj, uint32_t id); 248 | int ubus_unsubscribe(struct ubus_context *ctx, struct ubus_subscriber *obj, uint32_t id); 249 | 250 | /* ----------- rpc ----------- */ 251 | 252 | /* invoke a method on a specific object */ 253 | int ubus_invoke(struct ubus_context *ctx, uint32_t obj, const char *method, 254 | struct blob_attr *msg, ubus_data_handler_t cb, void *priv, 255 | int timeout); 256 | 257 | /* asynchronous version of ubus_invoke() */ 258 | int ubus_invoke_async(struct ubus_context *ctx, uint32_t obj, const char *method, 259 | struct blob_attr *msg, struct ubus_request *req); 260 | 261 | /* send a reply to an incoming object method call */ 262 | int ubus_send_reply(struct ubus_context *ctx, struct ubus_request_data *req, 263 | struct blob_attr *msg); 264 | 265 | static inline void ubus_defer_request(struct ubus_context *ctx, 266 | struct ubus_request_data *req, 267 | struct ubus_request_data *new_req) 268 | { 269 | memcpy(new_req, req, sizeof(*req)); 270 | req->deferred = true; 271 | } 272 | 273 | void ubus_complete_deferred_request(struct ubus_context *ctx, 274 | struct ubus_request_data *req, int ret); 275 | 276 | /* 277 | * send a notification to all subscribers of an object 278 | * if timeout < 0, no reply is expected from subscribers 279 | */ 280 | int ubus_notify(struct ubus_context *ctx, struct ubus_object *obj, 281 | const char *type, struct blob_attr *msg, int timeout); 282 | 283 | int ubus_notify_async(struct ubus_context *ctx, struct ubus_object *obj, 284 | const char *type, struct blob_attr *msg, 285 | struct ubus_notify_request *req); 286 | 287 | 288 | /* ----------- events ----------- */ 289 | 290 | int ubus_send_event(struct ubus_context *ctx, const char *id, 291 | struct blob_attr *data); 292 | 293 | int ubus_register_event_handler(struct ubus_context *ctx, 294 | struct ubus_event_handler *ev, 295 | const char *pattern); 296 | 297 | static inline int ubus_unregister_event_handler(struct ubus_context *ctx, 298 | struct ubus_event_handler *ev) 299 | { 300 | return ubus_remove_object(ctx, &ev->obj); 301 | } 302 | 303 | #endif 304 | -------------------------------------------------------------------------------- /lua/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.6) 2 | 3 | PROJECT(ubus C) 4 | 5 | SET(CMAKE_INSTALL_PREFIX /) 6 | 7 | IF(NOT LUA_CFLAGS) 8 | FIND_PROGRAM(PKG_CONFIG pkg-config) 9 | IF(PKG_CONFIG) 10 | EXECUTE_PROCESS( 11 | COMMAND pkg-config --silence-errors --cflags lua5.1 12 | OUTPUT_VARIABLE LUA_CFLAGS 13 | OUTPUT_STRIP_TRAILING_WHITESPACE 14 | ) 15 | ENDIF() 16 | ENDIF() 17 | 18 | ADD_DEFINITIONS(-Os -Wall -Werror --std=gnu99 -g3 -I.. ${LUA_CFLAGS}) 19 | INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/..) 20 | LINK_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/..) 21 | 22 | IF(APPLE) 23 | SET(CMAKE_SHARED_MODULE_CREATE_C_FLAGS "${CMAKE_SHARED_MODULE_CREATE_C_FLAGS} -undefined dynamic_lookup") 24 | ENDIF(APPLE) 25 | 26 | IF(NOT LUAPATH) 27 | EXECUTE_PROCESS( 28 | COMMAND lua -e "for k in string.gmatch(package.cpath .. \";\", \"([^;]+)/..so;\") do if k:sub(1,1) == \"/\" then print(k) break end end" 29 | OUTPUT_VARIABLE LUAPATH 30 | RESULT_VARIABLE LUA_CHECK_RES 31 | OUTPUT_STRIP_TRAILING_WHITESPACE 32 | ) 33 | 34 | IF(BUILD_LUA) 35 | IF(NOT ${LUA_CHECK_RES} EQUAL 0 OR "${LUAPATH}" EQUAL "") 36 | MESSAGE(SEND_ERROR "Lua was not found on your system") 37 | ENDIF() 38 | ENDIF() 39 | ENDIF() 40 | 41 | IF(BUILD_LUA) 42 | ADD_LIBRARY(ubus_lua MODULE ubus.c) 43 | SET_TARGET_PROPERTIES(ubus_lua PROPERTIES 44 | OUTPUT_NAME ubus 45 | PREFIX "" 46 | ) 47 | TARGET_LINK_LIBRARIES(ubus_lua ubus) 48 | 49 | INSTALL(TARGETS ubus_lua 50 | LIBRARY DESTINATION ${LUAPATH} 51 | ) 52 | ENDIF() 53 | -------------------------------------------------------------------------------- /lua/test.lua: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env lua 2 | 3 | require "ubus" 4 | require "uloop" 5 | 6 | uloop.init() 7 | 8 | local conn = ubus.connect() 9 | if not conn then 10 | error("Failed to connect to ubus") 11 | end 12 | 13 | local my_method = { 14 | broken = { 15 | hello = 1, 16 | hello1 = { 17 | function(req) 18 | end, {id = "fail" } 19 | }, 20 | }, 21 | test = { 22 | hello = { 23 | function(req, msg) 24 | conn:reply(req, {message="foo"}); 25 | print("Call to function 'hello'") 26 | for k, v in pairs(msg) do 27 | print("key=" .. k .. " value=" .. tostring(v)) 28 | end 29 | end, {id = ubus.INT32, msg = ubus.STRING } 30 | }, 31 | hello1 = { 32 | function(req) 33 | conn:reply(req, {message="foo1"}); 34 | conn:reply(req, {message="foo2"}); 35 | print("Call to function 'hello1'") 36 | end, {id = ubus.INT32, msg = ubus.STRING } 37 | } 38 | } 39 | } 40 | 41 | conn:add(my_method) 42 | 43 | local my_event = { 44 | test = function(msg) 45 | print("Call to test event") 46 | for k, v in pairs(msg) do 47 | print("key=" .. k .. " value=" .. tostring(v)) 48 | end 49 | end, 50 | } 51 | 52 | conn:listen(my_event) 53 | 54 | uloop.run() 55 | -------------------------------------------------------------------------------- /lua/test_client.lua: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env lua 2 | 3 | require "ubus" 4 | require "uloop" 5 | 6 | uloop.init() 7 | 8 | local conn = ubus.connect() 9 | if not conn then 10 | error("Failed to connect to ubusd") 11 | end 12 | 13 | local namespaces = conn:objects() 14 | for i, n in ipairs(namespaces) do 15 | print("namespace=" .. n) 16 | local signatures = conn:signatures(n) 17 | for p, s in pairs(signatures) do 18 | print("\tprocedure=" .. p) 19 | for k, v in pairs(s) do 20 | print("\t\tattribute=" .. k .. " type=" .. v) 21 | end 22 | end 23 | end 24 | 25 | local status = conn:call("test", "hello", { msg = "eth0" }) 26 | 27 | for k, v in pairs(status) do 28 | print("key=" .. k .. " value=" .. tostring(v)) 29 | end 30 | 31 | local status = {conn:call("test", "hello1", { msg = "eth0" })} 32 | 33 | for a = 1, #status do 34 | for k, v in pairs(status[a]) do 35 | print("key=" .. k .. " value=" .. tostring(v)) 36 | end 37 | end 38 | 39 | conn:send("test", { foo = "bar"}) 40 | 41 | uloop.run() 42 | -------------------------------------------------------------------------------- /lua/ubus.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2012 Jo-Philipp Wich 3 | * Copyright (C) 2012 John Crispin 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU Lesser General Public License version 2.1 7 | * as published by the Free Software Foundation 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | */ 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | #define MODNAME "ubus" 23 | #define METANAME MODNAME ".meta" 24 | 25 | static lua_State *state; 26 | 27 | struct ubus_lua_connection { 28 | int timeout; 29 | struct blob_buf buf; 30 | struct ubus_context *ctx; 31 | }; 32 | 33 | struct ubus_lua_object { 34 | struct ubus_object o; 35 | int r; 36 | }; 37 | 38 | struct ubus_lua_event { 39 | struct ubus_event_handler e; 40 | int r; 41 | }; 42 | 43 | static int 44 | ubus_lua_parse_blob(lua_State *L, struct blob_attr *attr, bool table); 45 | 46 | static int 47 | ubus_lua_parse_blob_array(lua_State *L, struct blob_attr *attr, int len, bool table) 48 | { 49 | int rv; 50 | int idx = 1; 51 | int rem = len; 52 | struct blob_attr *pos; 53 | 54 | lua_newtable(L); 55 | 56 | __blob_for_each_attr(pos, attr, rem) 57 | { 58 | rv = ubus_lua_parse_blob(L, pos, table); 59 | 60 | if (rv > 1) 61 | lua_rawset(L, -3); 62 | else if (rv > 0) 63 | lua_rawseti(L, -2, idx++); 64 | } 65 | 66 | return 1; 67 | } 68 | 69 | static int 70 | ubus_lua_parse_blob(lua_State *L, struct blob_attr *attr, bool table) 71 | { 72 | int len; 73 | int off = 0; 74 | void *data; 75 | 76 | if (!blobmsg_check_attr(attr, false)) 77 | return 0; 78 | 79 | if (table && blobmsg_name(attr)[0]) 80 | { 81 | lua_pushstring(L, blobmsg_name(attr)); 82 | off++; 83 | } 84 | 85 | data = blobmsg_data(attr); 86 | len = blobmsg_data_len(attr); 87 | 88 | switch (blob_id(attr)) 89 | { 90 | case BLOBMSG_TYPE_BOOL: 91 | lua_pushboolean(L, *(uint8_t *)data); 92 | break; 93 | 94 | case BLOBMSG_TYPE_INT16: 95 | lua_pushinteger(L, be16_to_cpu(*(uint16_t *)data)); 96 | break; 97 | 98 | case BLOBMSG_TYPE_INT32: 99 | lua_pushinteger(L, be32_to_cpu(*(uint32_t *)data)); 100 | break; 101 | 102 | case BLOBMSG_TYPE_INT64: 103 | lua_pushnumber(L, (double) be64_to_cpu(*(uint64_t *)data)); 104 | break; 105 | 106 | case BLOBMSG_TYPE_STRING: 107 | lua_pushstring(L, data); 108 | break; 109 | 110 | case BLOBMSG_TYPE_ARRAY: 111 | ubus_lua_parse_blob_array(L, data, len, false); 112 | break; 113 | 114 | case BLOBMSG_TYPE_TABLE: 115 | ubus_lua_parse_blob_array(L, data, len, true); 116 | break; 117 | 118 | default: 119 | lua_pushnil(L); 120 | break; 121 | } 122 | 123 | return off + 1; 124 | } 125 | 126 | 127 | static bool 128 | ubus_lua_format_blob_is_array(lua_State *L) 129 | { 130 | lua_Integer prv = 0; 131 | lua_Integer cur = 0; 132 | 133 | /* Find out whether table is array-like */ 134 | for (lua_pushnil(L); lua_next(L, -2); lua_pop(L, 1)) 135 | { 136 | #ifdef LUA_TINT 137 | if (lua_type(L, -2) != LUA_TNUMBER && lua_type(L, -2) != LUA_TINT) 138 | #else 139 | if (lua_type(L, -2) != LUA_TNUMBER) 140 | #endif 141 | { 142 | lua_pop(L, 2); 143 | return false; 144 | } 145 | 146 | cur = lua_tointeger(L, -2); 147 | 148 | if ((cur - 1) != prv) 149 | { 150 | lua_pop(L, 2); 151 | return false; 152 | } 153 | 154 | prv = cur; 155 | } 156 | 157 | return true; 158 | } 159 | 160 | static int 161 | ubus_lua_format_blob_array(lua_State *L, struct blob_buf *b, bool table); 162 | 163 | static int 164 | ubus_lua_format_blob(lua_State *L, struct blob_buf *b, bool table) 165 | { 166 | void *c; 167 | bool rv = true; 168 | const char *key = table ? lua_tostring(L, -2) : NULL; 169 | 170 | switch (lua_type(L, -1)) 171 | { 172 | case LUA_TBOOLEAN: 173 | blobmsg_add_u8(b, key, (uint8_t)lua_toboolean(L, -1)); 174 | break; 175 | 176 | #ifdef LUA_TINT 177 | case LUA_TINT: 178 | #endif 179 | case LUA_TNUMBER: 180 | blobmsg_add_u32(b, key, (uint32_t)lua_tointeger(L, -1)); 181 | break; 182 | 183 | case LUA_TSTRING: 184 | case LUA_TUSERDATA: 185 | case LUA_TLIGHTUSERDATA: 186 | blobmsg_add_string(b, key, lua_tostring(L, -1)); 187 | break; 188 | 189 | case LUA_TTABLE: 190 | if (ubus_lua_format_blob_is_array(L)) 191 | { 192 | c = blobmsg_open_array(b, key); 193 | rv = ubus_lua_format_blob_array(L, b, false); 194 | blobmsg_close_array(b, c); 195 | } 196 | else 197 | { 198 | c = blobmsg_open_table(b, key); 199 | rv = ubus_lua_format_blob_array(L, b, true); 200 | blobmsg_close_table(b, c); 201 | } 202 | break; 203 | 204 | default: 205 | rv = false; 206 | break; 207 | } 208 | 209 | return rv; 210 | } 211 | 212 | static int 213 | ubus_lua_format_blob_array(lua_State *L, struct blob_buf *b, bool table) 214 | { 215 | for (lua_pushnil(L); lua_next(L, -2); lua_pop(L, 1)) 216 | { 217 | if (!ubus_lua_format_blob(L, b, table)) 218 | { 219 | lua_pop(L, 1); 220 | return false; 221 | } 222 | } 223 | 224 | return true; 225 | } 226 | 227 | 228 | static int 229 | ubus_lua_connect(lua_State *L) 230 | { 231 | struct ubus_lua_connection *c; 232 | const char *sockpath = luaL_optstring(L, 1, NULL); 233 | int timeout = luaL_optint(L, 2, 30); 234 | 235 | if ((c = lua_newuserdata(L, sizeof(*c))) != NULL && 236 | (c->ctx = ubus_connect(sockpath)) != NULL) 237 | { 238 | ubus_add_uloop(c->ctx); 239 | c->timeout = timeout; 240 | memset(&c->buf, 0, sizeof(c->buf)); 241 | luaL_getmetatable(L, METANAME); 242 | lua_setmetatable(L, -2); 243 | return 1; 244 | } 245 | 246 | /* NB: no errors from ubus_connect() yet */ 247 | lua_pushnil(L); 248 | lua_pushinteger(L, UBUS_STATUS_UNKNOWN_ERROR); 249 | return 2; 250 | } 251 | 252 | 253 | static void 254 | ubus_lua_objects_cb(struct ubus_context *c, struct ubus_object_data *o, void *p) 255 | { 256 | lua_State *L = (lua_State *)p; 257 | 258 | lua_pushstring(L, o->path); 259 | lua_rawseti(L, -2, lua_objlen(L, -2) + 1); 260 | } 261 | 262 | static int 263 | ubus_lua_objects(lua_State *L) 264 | { 265 | int rv; 266 | struct ubus_lua_connection *c = luaL_checkudata(L, 1, METANAME); 267 | 268 | lua_newtable(L); 269 | rv = ubus_lookup(c->ctx, NULL, ubus_lua_objects_cb, L); 270 | 271 | if (rv != UBUS_STATUS_OK) 272 | { 273 | lua_pop(L, 1); 274 | lua_pushnil(L); 275 | lua_pushinteger(L, rv); 276 | return 2; 277 | } 278 | 279 | return 1; 280 | } 281 | 282 | static int 283 | ubus_method_handler(struct ubus_context *ctx, struct ubus_object *obj, 284 | struct ubus_request_data *req, const char *method, 285 | struct blob_attr *msg) 286 | { 287 | struct ubus_lua_object *o = container_of(obj, struct ubus_lua_object, o); 288 | 289 | lua_getglobal(state, "__ubus_cb"); 290 | lua_rawgeti(state, -1, o->r); 291 | lua_getfield(state, -1, method); 292 | 293 | if (lua_isfunction(state, -1)) { 294 | lua_pushlightuserdata(state, req); 295 | if (!msg) 296 | lua_pushnil(state); 297 | else 298 | ubus_lua_parse_blob_array(state, blob_data(msg), blob_len(msg), true); 299 | lua_call(state, 2, 0); 300 | } 301 | return 0; 302 | } 303 | 304 | static int lua_gettablelen(lua_State *L, int index) 305 | { 306 | int cnt = 0; 307 | 308 | lua_pushnil(L); 309 | index -= 1; 310 | while (lua_next(L, index) != 0) { 311 | cnt++; 312 | lua_pop(L, 1); 313 | } 314 | 315 | return cnt; 316 | } 317 | 318 | static int ubus_lua_reply(lua_State *L) 319 | { 320 | struct ubus_lua_connection *c = luaL_checkudata(L, 1, METANAME); 321 | struct ubus_request_data *req; 322 | 323 | luaL_checktype(L, 3, LUA_TTABLE); 324 | blob_buf_init(&c->buf, 0); 325 | 326 | if (!ubus_lua_format_blob_array(L, &c->buf, true)) 327 | { 328 | lua_pushnil(L); 329 | lua_pushinteger(L, UBUS_STATUS_INVALID_ARGUMENT); 330 | return 2; 331 | } 332 | 333 | req = lua_touserdata(L, 2); 334 | ubus_send_reply(c->ctx, req, c->buf.head); 335 | 336 | return 0; 337 | } 338 | 339 | static int ubus_lua_load_methods(lua_State *L, struct ubus_method *m) 340 | { 341 | struct blobmsg_policy *p; 342 | int plen; 343 | int pidx = 0; 344 | 345 | /* get the function pointer */ 346 | lua_pushinteger(L, 1); 347 | lua_gettable(L, -2); 348 | 349 | /* get the policy table */ 350 | lua_pushinteger(L, 2); 351 | lua_gettable(L, -3); 352 | plen = lua_gettablelen(L, -1); 353 | 354 | /* check if the method table is valid */ 355 | if ((lua_type(L, -2) != LUA_TFUNCTION) || 356 | (lua_type(L, -1) != LUA_TTABLE) || 357 | lua_objlen(L, -1) || !plen) { 358 | lua_pop(L, 2); 359 | return 1; 360 | } 361 | 362 | /* store function pointer */ 363 | lua_pushvalue(L, -2); 364 | lua_setfield(L, -6, lua_tostring(L, -5)); 365 | 366 | /* setup the policy pointers */ 367 | p = malloc(sizeof(struct blobmsg_policy) * plen); 368 | memset(p, 0, sizeof(struct blobmsg_policy) * plen); 369 | m->policy = p; 370 | lua_pushnil(L); 371 | while (lua_next(L, -2) != 0) { 372 | int val = lua_tointeger(L, -1); 373 | 374 | /* check if the policy is valid */ 375 | if ((lua_type(L, -2) != LUA_TSTRING) || 376 | (lua_type(L, -1) != LUA_TNUMBER) || 377 | (val < 0) || 378 | (val > BLOBMSG_TYPE_LAST)) { 379 | lua_pop(L, 1); 380 | continue; 381 | } 382 | p[pidx].name = lua_tostring(L, -2); 383 | p[pidx].type = val; 384 | lua_pop(L, 1); 385 | pidx++; 386 | } 387 | 388 | m->n_policy = pidx; 389 | m->name = lua_tostring(L, -4); 390 | m->handler = ubus_method_handler; 391 | lua_pop(L, 2); 392 | 393 | return 0; 394 | } 395 | 396 | static struct ubus_object* ubus_lua_load_object(lua_State *L) 397 | { 398 | struct ubus_lua_object *obj = NULL; 399 | int mlen = lua_gettablelen(L, -1); 400 | struct ubus_method *m; 401 | int midx = 0; 402 | 403 | /* setup object pointers */ 404 | obj = malloc(sizeof(struct ubus_lua_object)); 405 | memset(obj, 0, sizeof(struct ubus_lua_object)); 406 | obj->o.name = lua_tostring(L, -2); 407 | 408 | /* setup method pointers */ 409 | m = malloc(sizeof(struct ubus_method) * mlen); 410 | memset(m, 0, sizeof(struct ubus_method) * mlen); 411 | obj->o.methods = m; 412 | 413 | /* setup type pointers */ 414 | obj->o.type = malloc(sizeof(struct ubus_object_type)); 415 | memset(obj->o.type, 0, sizeof(struct ubus_object_type)); 416 | obj->o.type->name = lua_tostring(L, -2); 417 | obj->o.type->id = 0; 418 | obj->o.type->methods = obj->o.methods; 419 | 420 | /* create the he callback lookup table */ 421 | lua_createtable(L, 1, 0); 422 | lua_getglobal(L, "__ubus_cb"); 423 | lua_pushvalue(L, -2); 424 | obj->r = luaL_ref(L, -2); 425 | lua_pop(L, 1); 426 | 427 | /* scan each method */ 428 | lua_pushnil(L); 429 | while (lua_next(L, -3) != 0) { 430 | /* check if it looks like a method */ 431 | if ((lua_type(L, -2) != LUA_TSTRING) || 432 | (lua_type(L, -1) != LUA_TTABLE) || 433 | !lua_objlen(L, -1)) { 434 | lua_pop(L, 1); 435 | continue; 436 | } 437 | 438 | if (!ubus_lua_load_methods(L, &m[midx])) 439 | midx++; 440 | lua_pop(L, 1); 441 | } 442 | 443 | obj->o.type->n_methods = obj->o.n_methods = midx; 444 | 445 | /* pop the callback table */ 446 | lua_pop(L, 1); 447 | 448 | return &obj->o; 449 | } 450 | 451 | static int ubus_lua_add(lua_State *L) 452 | { 453 | struct ubus_lua_connection *c = luaL_checkudata(L, 1, METANAME); 454 | 455 | /* verify top level object */ 456 | if (lua_istable(L, 1)) { 457 | lua_pushstring(L, "you need to pass a table"); 458 | lua_error(L); 459 | return 0; 460 | } 461 | 462 | /* scan each object */ 463 | lua_pushnil(L); 464 | while (lua_next(L, -2) != 0) { 465 | struct ubus_object *obj = NULL; 466 | 467 | /* check if the object has a table of methods */ 468 | if ((lua_type(L, -2) == LUA_TSTRING) && (lua_type(L, -1) == LUA_TTABLE)) { 469 | obj = ubus_lua_load_object(L); 470 | 471 | if (obj) 472 | ubus_add_object(c->ctx, obj); 473 | } 474 | lua_pop(L, 1); 475 | } 476 | 477 | return 0; 478 | } 479 | 480 | static void 481 | ubus_lua_signatures_cb(struct ubus_context *c, struct ubus_object_data *o, void *p) 482 | { 483 | lua_State *L = (lua_State *)p; 484 | 485 | if (!o->signature) 486 | return; 487 | 488 | ubus_lua_parse_blob_array(L, blob_data(o->signature), blob_len(o->signature), true); 489 | } 490 | 491 | static int 492 | ubus_lua_signatures(lua_State *L) 493 | { 494 | int rv; 495 | struct ubus_lua_connection *c = luaL_checkudata(L, 1, METANAME); 496 | const char *path = luaL_checkstring(L, 2); 497 | 498 | rv = ubus_lookup(c->ctx, path, ubus_lua_signatures_cb, L); 499 | 500 | if (rv != UBUS_STATUS_OK) 501 | { 502 | lua_pop(L, 1); 503 | lua_pushnil(L); 504 | lua_pushinteger(L, rv); 505 | return 2; 506 | } 507 | 508 | return 1; 509 | } 510 | 511 | 512 | static void 513 | ubus_lua_call_cb(struct ubus_request *req, int type, struct blob_attr *msg) 514 | { 515 | lua_State *L = (lua_State *)req->priv; 516 | 517 | if (!msg) 518 | lua_pushnil(L); 519 | 520 | ubus_lua_parse_blob_array(L, blob_data(msg), blob_len(msg), true); 521 | } 522 | 523 | static int 524 | ubus_lua_call(lua_State *L) 525 | { 526 | int rv, top; 527 | uint32_t id; 528 | struct ubus_lua_connection *c = luaL_checkudata(L, 1, METANAME); 529 | const char *path = luaL_checkstring(L, 2); 530 | const char *func = luaL_checkstring(L, 3); 531 | 532 | luaL_checktype(L, 4, LUA_TTABLE); 533 | blob_buf_init(&c->buf, 0); 534 | 535 | if (!ubus_lua_format_blob_array(L, &c->buf, true)) 536 | { 537 | lua_pushnil(L); 538 | lua_pushinteger(L, UBUS_STATUS_INVALID_ARGUMENT); 539 | return 2; 540 | } 541 | 542 | rv = ubus_lookup_id(c->ctx, path, &id); 543 | 544 | if (rv) 545 | { 546 | lua_pushnil(L); 547 | lua_pushinteger(L, rv); 548 | return 2; 549 | } 550 | 551 | top = lua_gettop(L); 552 | rv = ubus_invoke(c->ctx, id, func, c->buf.head, ubus_lua_call_cb, L, c->timeout * 1000); 553 | 554 | if (rv != UBUS_STATUS_OK) 555 | { 556 | lua_pop(L, 1); 557 | lua_pushnil(L); 558 | lua_pushinteger(L, rv); 559 | return 2; 560 | } 561 | 562 | return lua_gettop(L) - top; 563 | } 564 | 565 | static void 566 | ubus_event_handler(struct ubus_context *ctx, struct ubus_event_handler *ev, 567 | const char *type, struct blob_attr *msg) 568 | { 569 | struct ubus_lua_event *listener = container_of(ev, struct ubus_lua_event, e); 570 | 571 | lua_getglobal(state, "__ubus_cb_event"); 572 | lua_rawgeti(state, -1, listener->r); 573 | 574 | if (lua_isfunction(state, -1)) { 575 | ubus_lua_parse_blob_array(state, blob_data(msg), blob_len(msg), true); 576 | lua_call(state, 1, 0); 577 | } 578 | } 579 | 580 | static struct ubus_event_handler* 581 | ubus_lua_load_event(lua_State *L) 582 | { 583 | struct ubus_lua_event* event = NULL; 584 | 585 | event = malloc(sizeof(struct ubus_lua_event)); 586 | memset(event, 0, sizeof(struct ubus_lua_event)); 587 | event->e.cb = ubus_event_handler; 588 | 589 | /* update the he callback lookup table */ 590 | lua_getglobal(L, "__ubus_cb_event"); 591 | lua_pushvalue(L, -2); 592 | event->r = luaL_ref(L, -2); 593 | lua_setfield(L, -1, lua_tostring(L, -3)); 594 | 595 | return &event->e; 596 | } 597 | 598 | static int 599 | ubus_lua_listen(lua_State *L) { 600 | struct ubus_lua_connection *c = luaL_checkudata(L, 1, METANAME); 601 | 602 | /* verify top level object */ 603 | luaL_checktype(L, 2, LUA_TTABLE); 604 | 605 | /* scan each object */ 606 | lua_pushnil(L); 607 | while (lua_next(L, -2) != 0) { 608 | struct ubus_event_handler *listener; 609 | 610 | /* check if the key is a string and the value is a method */ 611 | if ((lua_type(L, -2) == LUA_TSTRING) && (lua_type(L, -1) == LUA_TFUNCTION)) { 612 | listener = ubus_lua_load_event(L); 613 | if(listener != NULL) { 614 | ubus_register_event_handler(c->ctx, listener, lua_tostring(L, -2)); 615 | } 616 | } 617 | lua_pop(L, 1); 618 | } 619 | return 0; 620 | } 621 | 622 | static int 623 | ubus_lua_send(lua_State *L) 624 | { 625 | struct ubus_lua_connection *c = luaL_checkudata(L, 1, METANAME); 626 | const char *event = luaL_checkstring(L, 2); 627 | 628 | if (*event == 0) 629 | return luaL_argerror(L, 2, "no event name"); 630 | 631 | // Event content convert to ubus form 632 | luaL_checktype(L, 3, LUA_TTABLE); 633 | blob_buf_init(&c->buf, 0); 634 | 635 | if (!ubus_lua_format_blob_array(L, &c->buf, true)) { 636 | lua_pushnil(L); 637 | lua_pushinteger(L, UBUS_STATUS_INVALID_ARGUMENT); 638 | return 2; 639 | } 640 | 641 | // Send the event 642 | ubus_send_event(c->ctx, event, c->buf.head); 643 | 644 | return 0; 645 | } 646 | 647 | 648 | 649 | static int 650 | ubus_lua__gc(lua_State *L) 651 | { 652 | struct ubus_lua_connection *c = luaL_checkudata(L, 1, METANAME); 653 | 654 | if (c->ctx != NULL) 655 | { 656 | ubus_free(c->ctx); 657 | memset(c, 0, sizeof(*c)); 658 | } 659 | 660 | return 0; 661 | } 662 | 663 | static const luaL_Reg ubus[] = { 664 | { "connect", ubus_lua_connect }, 665 | { "objects", ubus_lua_objects }, 666 | { "add", ubus_lua_add }, 667 | { "reply", ubus_lua_reply }, 668 | { "signatures", ubus_lua_signatures }, 669 | { "call", ubus_lua_call }, 670 | { "close", ubus_lua__gc }, 671 | { "listen", ubus_lua_listen }, 672 | { "send", ubus_lua_send }, 673 | { "__gc", ubus_lua__gc }, 674 | { NULL, NULL }, 675 | }; 676 | 677 | /* avoid missing prototype warning */ 678 | int luaopen_ubus(lua_State *L); 679 | 680 | int 681 | luaopen_ubus(lua_State *L) 682 | { 683 | /* create metatable */ 684 | luaL_newmetatable(L, METANAME); 685 | 686 | /* metatable.__index = metatable */ 687 | lua_pushvalue(L, -1); 688 | lua_setfield(L, -2, "__index"); 689 | 690 | /* fill metatable */ 691 | luaL_register(L, NULL, ubus); 692 | lua_pop(L, 1); 693 | 694 | /* create module */ 695 | luaL_register(L, MODNAME, ubus); 696 | 697 | /* set some enum defines */ 698 | lua_pushinteger(L, BLOBMSG_TYPE_ARRAY); 699 | lua_setfield(L, -2, "ARRAY"); 700 | lua_pushinteger(L, BLOBMSG_TYPE_TABLE); 701 | lua_setfield(L, -2, "TABLE"); 702 | lua_pushinteger(L, BLOBMSG_TYPE_STRING); 703 | lua_setfield(L, -2, "STRING"); 704 | lua_pushinteger(L, BLOBMSG_TYPE_INT64); 705 | lua_setfield(L, -2, "INT64"); 706 | lua_pushinteger(L, BLOBMSG_TYPE_INT32); 707 | lua_setfield(L, -2, "INT32"); 708 | lua_pushinteger(L, BLOBMSG_TYPE_INT16); 709 | lua_setfield(L, -2, "INT16"); 710 | lua_pushinteger(L, BLOBMSG_TYPE_INT8); 711 | lua_setfield(L, -2, "INT8"); 712 | lua_pushinteger(L, BLOBMSG_TYPE_BOOL); 713 | lua_setfield(L, -2, "BOOLEAN"); 714 | 715 | /* used in our callbacks */ 716 | state = L; 717 | 718 | /* create the callback table */ 719 | lua_createtable(L, 1, 0); 720 | lua_setglobal(L, "__ubus_cb"); 721 | 722 | /* create the event table */ 723 | lua_createtable(L, 1, 0); 724 | lua_setglobal(L, "__ubus_cb_event"); 725 | 726 | return 0; 727 | } 728 | -------------------------------------------------------------------------------- /python/.gitignore: -------------------------------------------------------------------------------- 1 | *.gch 2 | build/ 3 | *.egg-info 4 | *.pyc 5 | __pycache__/ 6 | .tox/ 7 | -------------------------------------------------------------------------------- /python/MANIFEST.in: -------------------------------------------------------------------------------- 1 | include MANIFEST.in 2 | include *.rst 3 | include ubus/libubus.processed.h 4 | -------------------------------------------------------------------------------- /python/setup.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | 3 | import re 4 | 5 | from setuptools import setup, find_packages 6 | 7 | try: 8 | import ubus 9 | except ImportError: 10 | # We're installing, so cffi isn't available yet 11 | ext_modules = [] 12 | else: 13 | # We're building bdist, so cffi is available 14 | ext_modules = [ubus.ffi.verifier.get_extension()] 15 | 16 | 17 | def get_version(filename): 18 | init_py = open(filename).read() 19 | metadata = dict(re.findall("__([a-z]+)__ = '([^']+)'", init_py)) 20 | return metadata['version'] 21 | 22 | 23 | setup( 24 | name='ubus', 25 | version=get_version('ubus/__init__.py'), 26 | url='http://github.com/txomon/ubus', 27 | license='Apache License, Version 2.0', 28 | author='Javier Domingo Cansino', 29 | author_email='javierdo1@gmail.com', 30 | description='Python wrapper for libubus', 31 | # long_description=open('README.rst').read(), 32 | packages=find_packages(exclude=['tests', 'tests.*']), 33 | zip_safe=False, 34 | include_package_data=True, 35 | ext_package='ubus', 36 | ext_modules=ext_modules, 37 | install_requires=[ 38 | 'cffi >= 0.6', 39 | ] 40 | ) 41 | -------------------------------------------------------------------------------- /python/tests/__init__.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | 3 | import gc 4 | import platform 5 | 6 | import cffi 7 | 8 | cffi.verifier.cleanup_tmpdir() 9 | 10 | 11 | def gc_collect(): 12 | """Run enough GC collections to make object finalizers run.""" 13 | if platform.python_implementation() == 'PyPy': 14 | # Since PyPy use garbage collection instead of reference counting 15 | # objects are not finalized before the next major GC collection. 16 | # Currently, the best way we have to ensure a major GC collection has 17 | # run is to call gc.collect() a number of times. 18 | [gc.collect() for _ in range(10)] 19 | else: 20 | gc.collect() 21 | -------------------------------------------------------------------------------- /python/tests/test_lib.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | 3 | import ubus 4 | import unittest 5 | 6 | 7 | class LibraryTest(unittest.TestCase): 8 | def test_ubus_error_to_string_binding_works(self): 9 | self.assertEqual( 10 | ubus.ffi.string(ubus.lib.ubus_strerror(0)), 11 | b'Success' 12 | ) 13 | 14 | def test_ubus_max_notify_peers_macro(self): 15 | self.assertEqual(ubus.lib.UBUS_MAX_NOTIFY_PEERS, 16) 16 | -------------------------------------------------------------------------------- /python/tests/test_session.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | 3 | import mock 4 | import unittest 5 | 6 | import ubus 7 | 8 | class SessionTest(unittest.TestCase): 9 | 10 | def test_create_session(self): 11 | """Test that connecting to the standard socket fails with a 12 | connection error due to access persmissions. 13 | """ 14 | self.assertRaises(ubus.UbusError, ubus.Session) 15 | 16 | def test_create_session_path_to_socket(self): 17 | """Test that connecting to the default socket althought specifying 18 | route also faild due to permission errors. 19 | """ 20 | self.assertRaises(ubus.UbusError, ubus.Session, "/var/run/ubus.sock") 21 | 22 | def test_create_session_to_writable_socket(self): 23 | """Test that if we specify a writtable socket, stuff works 24 | """ 25 | session = ubus.Session("/tmp/socket") 26 | self.assertIsNotNone(session) 27 | -------------------------------------------------------------------------------- /python/tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | envlist = py27, flake8 3 | 4 | [testenv] 5 | deps = 6 | cffi 7 | mock 8 | nose 9 | 10 | commands = nosetests 11 | 12 | [testenv:flake8] 13 | deps = flake8 14 | commands = flake8 ubus/ tests/ 15 | -------------------------------------------------------------------------------- /python/ubus/__init__.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | 3 | import logging 4 | import os 5 | import weakref 6 | 7 | import cffi 8 | 9 | 10 | __version__ = '0.0.0' 11 | 12 | 13 | # Log to nowhere by default. For details, see: 14 | # http://docs.python.org/2/howto/logging.html#library-config 15 | logging.getLogger('ubus').addHandler(logging.NullHandler()) 16 | 17 | 18 | _header_file = os.path.join(os.path.dirname(__file__), 'libubus.processed.h') 19 | _header = open(_header_file).read() 20 | ffi = cffi.FFI() 21 | ffi.cdef(_header) 22 | lib = ffi.verify(""" 23 | #include 24 | """, 25 | libraries=[str('ubus'), 26 | str('ubox'), 27 | str('blobmsg_json'), 28 | str('json_script')], 29 | ext_package='ubus') 30 | 31 | 32 | # Mapping between keys and objects that should be kept alive as long as the key 33 | # is alive. May be used to keep objects alive when there isn't a more 34 | # convenient place to keep a reference to it. The keys are weakrefs, so entries 35 | # disappear from the dict when the key is garbage collected, potentially 36 | # causing objects associated to the key to be garbage collected as well. For 37 | # further details, refer to the CFFI docs. 38 | weak_key_dict = weakref.WeakKeyDictionary() 39 | 40 | # Mapping between simple keys, like ints and bytestrings that can be passed as 41 | # userdata through C context, and Python objects used to call the corresponding 42 | # callback functions in Python context. 43 | callback_dict = {} 44 | 45 | # Reference to ubus Session instance. Used to enforce that one and only 46 | # one session exists in each process. 47 | session_instance = None 48 | 49 | 50 | from ubus.session import * # noqa 51 | -------------------------------------------------------------------------------- /python/ubus/libubus.processed.h: -------------------------------------------------------------------------------- 1 | # 1 "../libubus.h" 2 | # 1 "" 3 | # 1 "../libubus.h" 4 | # 22 "../libubus.h" 5 | # 1 "../ubusmsg.h" 1 6 | # 27 "../ubusmsg.h" 7 | struct ubus_msghdr { 8 | uint8_t version; 9 | uint8_t type; 10 | uint16_t seq; 11 | uint32_t peer; 12 | }; 13 | struct list_head { ...; }; 14 | 15 | struct avl_tree { ...; }; 16 | struct uloop_fd { ...; }; 17 | struct avl_node { ...; }; 18 | enum ubus_msg_type { 19 | UBUS_MSG_HELLO, 20 | UBUS_MSG_STATUS, 21 | UBUS_MSG_DATA, 22 | UBUS_MSG_PING, 23 | UBUS_MSG_LOOKUP, 24 | UBUS_MSG_INVOKE, 25 | UBUS_MSG_ADD_OBJECT, 26 | UBUS_MSG_REMOVE_OBJECT, 27 | UBUS_MSG_SUBSCRIBE, 28 | UBUS_MSG_UNSUBSCRIBE, 29 | UBUS_MSG_NOTIFY, 30 | __UBUS_MSG_LAST, 31 | }; 32 | 33 | enum ubus_msg_attr { 34 | UBUS_ATTR_UNSPEC, 35 | UBUS_ATTR_STATUS, 36 | UBUS_ATTR_OBJPATH, 37 | UBUS_ATTR_OBJID, 38 | UBUS_ATTR_METHOD, 39 | UBUS_ATTR_OBJTYPE, 40 | UBUS_ATTR_SIGNATURE, 41 | UBUS_ATTR_DATA, 42 | UBUS_ATTR_TARGET, 43 | UBUS_ATTR_ACTIVE, 44 | UBUS_ATTR_NO_REPLY, 45 | UBUS_ATTR_SUBSCRIBERS, 46 | UBUS_ATTR_MAX, 47 | }; 48 | 49 | enum ubus_msg_status { 50 | UBUS_STATUS_OK, 51 | UBUS_STATUS_INVALID_COMMAND, 52 | UBUS_STATUS_INVALID_ARGUMENT, 53 | UBUS_STATUS_METHOD_NOT_FOUND, 54 | UBUS_STATUS_NOT_FOUND, 55 | UBUS_STATUS_NO_DATA, 56 | UBUS_STATUS_PERMISSION_DENIED, 57 | UBUS_STATUS_TIMEOUT, 58 | UBUS_STATUS_NOT_SUPPORTED, 59 | UBUS_STATUS_UNKNOWN_ERROR, 60 | UBUS_STATUS_CONNECTION_FAILED, 61 | __UBUS_STATUS_LAST 62 | }; 63 | # 23 "../libubus.h" 2 64 | # 1 "../ubus_common.h" 1 65 | # 24 "../libubus.h" 2 66 | 67 | 68 | 69 | struct ubus_context; 70 | struct ubus_msg_src; 71 | struct ubus_object; 72 | struct ubus_request; 73 | struct ubus_request_data; 74 | struct ubus_object_data; 75 | struct ubus_event_handler; 76 | struct ubus_subscriber; 77 | struct ubus_notify_request; 78 | 79 | static inline struct blob_attr * 80 | ubus_msghdr_data(struct ubus_msghdr *hdr) ; 81 | 82 | typedef void (*ubus_lookup_handler_t)(struct ubus_context *ctx, 83 | struct ubus_object_data *obj, 84 | void *priv); 85 | typedef int (*ubus_handler_t)(struct ubus_context *ctx, struct ubus_object *obj, 86 | struct ubus_request_data *req, 87 | const char *method, struct blob_attr *msg); 88 | typedef void (*ubus_state_handler_t)(struct ubus_context *ctx, struct ubus_object *obj); 89 | typedef void (*ubus_remove_handler_t)(struct ubus_context *ctx, 90 | struct ubus_subscriber *obj, uint32_t id); 91 | typedef void (*ubus_event_handler_t)(struct ubus_context *ctx, struct ubus_event_handler *ev, 92 | const char *type, struct blob_attr *msg); 93 | typedef void (*ubus_data_handler_t)(struct ubus_request *req, 94 | int type, struct blob_attr *msg); 95 | typedef void (*ubus_complete_handler_t)(struct ubus_request *req, int ret); 96 | typedef void (*ubus_notify_complete_handler_t)(struct ubus_notify_request *req, 97 | int idx, int ret); 98 | # 82 "../libubus.h" 99 | struct ubus_method { 100 | const char *name; 101 | ubus_handler_t handler; 102 | 103 | const struct blobmsg_policy *policy; 104 | int n_policy; 105 | }; 106 | 107 | struct ubus_object_type { 108 | const char *name; 109 | uint32_t id; 110 | 111 | const struct ubus_method *methods; 112 | int n_methods; 113 | }; 114 | 115 | struct ubus_object { 116 | struct avl_node avl; 117 | 118 | const char *name; 119 | uint32_t id; 120 | 121 | const char *path; 122 | struct ubus_object_type *type; 123 | 124 | ubus_state_handler_t subscribe_cb; 125 | bool has_subscribers; 126 | 127 | const struct ubus_method *methods; 128 | int n_methods; 129 | }; 130 | 131 | struct ubus_subscriber { 132 | struct ubus_object obj; 133 | 134 | ubus_handler_t cb; 135 | ubus_remove_handler_t remove_cb; 136 | }; 137 | 138 | struct ubus_event_handler { 139 | struct ubus_object obj; 140 | 141 | ubus_event_handler_t cb; 142 | }; 143 | 144 | struct ubus_context { 145 | struct list_head requests; 146 | struct avl_tree objects; 147 | struct list_head pending; 148 | 149 | struct uloop_fd sock; 150 | 151 | uint32_t local_id; 152 | uint16_t request_seq; 153 | int stack_depth; 154 | 155 | void (*connection_lost)(struct ubus_context *ctx); 156 | 157 | struct { 158 | struct ubus_msghdr hdr; 159 | char data[65536]; 160 | } msgbuf; 161 | }; 162 | 163 | struct ubus_object_data { 164 | uint32_t id; 165 | uint32_t type_id; 166 | const char *path; 167 | struct blob_attr *signature; 168 | }; 169 | 170 | struct ubus_request_data { 171 | uint32_t object; 172 | uint32_t peer; 173 | uint16_t seq; 174 | bool deferred; 175 | }; 176 | 177 | struct ubus_request { 178 | struct list_head list; 179 | 180 | struct list_head pending; 181 | int status_code; 182 | bool status_msg; 183 | bool blocked; 184 | bool cancelled; 185 | bool notify; 186 | 187 | uint32_t peer; 188 | uint16_t seq; 189 | 190 | ubus_data_handler_t raw_data_cb; 191 | ubus_data_handler_t data_cb; 192 | ubus_complete_handler_t complete_cb; 193 | 194 | struct ubus_context *ctx; 195 | void *priv; 196 | }; 197 | 198 | struct ubus_notify_request { 199 | struct ubus_request req; 200 | 201 | ubus_notify_complete_handler_t status_cb; 202 | ubus_notify_complete_handler_t complete_cb; 203 | 204 | uint32_t pending; 205 | uint32_t id[...]; 206 | }; 207 | 208 | struct ubus_context *ubus_connect(const char *path); 209 | int ubus_reconnect(struct ubus_context *ctx, const char *path); 210 | void ubus_free(struct ubus_context *ctx); 211 | 212 | const char *ubus_strerror(int error); 213 | 214 | static inline void ubus_add_uloop(struct ubus_context *ctx); 215 | 216 | 217 | static inline void ubus_handle_event(struct ubus_context *ctx); 218 | 219 | 220 | 221 | 222 | int ubus_complete_request(struct ubus_context *ctx, struct ubus_request *req, 223 | int timeout); 224 | 225 | 226 | void ubus_complete_request_async(struct ubus_context *ctx, 227 | struct ubus_request *req); 228 | 229 | 230 | void ubus_abort_request(struct ubus_context *ctx, struct ubus_request *req); 231 | 232 | 233 | 234 | int ubus_lookup(struct ubus_context *ctx, const char *path, 235 | ubus_lookup_handler_t cb, void *priv); 236 | 237 | int ubus_lookup_id(struct ubus_context *ctx, const char *path, uint32_t *id); 238 | 239 | 240 | int ubus_add_object(struct ubus_context *ctx, struct ubus_object *obj); 241 | 242 | 243 | int ubus_remove_object(struct ubus_context *ctx, struct ubus_object *obj); 244 | 245 | 246 | int ubus_register_subscriber(struct ubus_context *ctx, struct ubus_subscriber *obj); 247 | 248 | static inline int 249 | ubus_unregister_subscriber(struct ubus_context *ctx, struct ubus_subscriber *obj); 250 | 251 | int ubus_subscribe(struct ubus_context *ctx, struct ubus_subscriber *obj, uint32_t id); 252 | int ubus_unsubscribe(struct ubus_context *ctx, struct ubus_subscriber *obj, uint32_t id); 253 | 254 | 255 | 256 | 257 | int ubus_invoke(struct ubus_context *ctx, uint32_t obj, const char *method, 258 | struct blob_attr *msg, ubus_data_handler_t cb, void *priv, 259 | int timeout); 260 | 261 | 262 | int ubus_invoke_async(struct ubus_context *ctx, uint32_t obj, const char *method, 263 | struct blob_attr *msg, struct ubus_request *req); 264 | 265 | 266 | int ubus_send_reply(struct ubus_context *ctx, struct ubus_request_data *req, 267 | struct blob_attr *msg); 268 | 269 | static inline void ubus_defer_request(struct ubus_context *ctx, 270 | struct ubus_request_data *req, 271 | struct ubus_request_data *new_req); 272 | 273 | void ubus_complete_deferred_request(struct ubus_context *ctx, 274 | struct ubus_request_data *req, int ret); 275 | 276 | 277 | 278 | 279 | 280 | int ubus_notify(struct ubus_context *ctx, struct ubus_object *obj, 281 | const char *type, struct blob_attr *msg, int timeout); 282 | 283 | int ubus_notify_async(struct ubus_context *ctx, struct ubus_object *obj, 284 | const char *type, struct blob_attr *msg, 285 | struct ubus_notify_request *req); 286 | 287 | 288 | 289 | 290 | int ubus_send_event(struct ubus_context *ctx, const char *id, 291 | struct blob_attr *data); 292 | 293 | int ubus_register_event_handler(struct ubus_context *ctx, 294 | struct ubus_event_handler *ev, 295 | const char *pattern); 296 | 297 | static inline int ubus_unregister_event_handler(struct ubus_context *ctx, 298 | struct ubus_event_handler *ev); 299 | -------------------------------------------------------------------------------- /python/ubus/session.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | 3 | import logging 4 | 5 | import ubus 6 | from ubus import ffi, lib 7 | 8 | class Session(object): 9 | def __init__(self, socket=None): 10 | pass 11 | -------------------------------------------------------------------------------- /systemd/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | CONFIGURE_FILE(ubus.socket.in ubus.socket) 2 | CONFIGURE_FILE(ubus.service.in ubus.service) 3 | 4 | # Cmakes pkgconfig support is very limited, so for now just hardcode 5 | SET(SYSTEMD_SYSUNIT_DIR "${SYSTEMD_PREFIX}/lib/systemd/system") 6 | INSTALL(FILES ${CMAKE_BINARY_DIR}/systemd/ubus.socket ${CMAKE_BINARY_DIR}/systemd/ubus.service 7 | DESTINATION ${SYSTEMD_SYSUNIT_DIR}) 8 | -------------------------------------------------------------------------------- /systemd/ubus.service.in: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=OpenWrt micro bus 3 | Requires=ubus.socket 4 | 5 | [Service] 6 | ExecStart=@UBUSD_BINARY@ 7 | -------------------------------------------------------------------------------- /systemd/ubus.socket.in: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=OpenWrt micro bus socket 3 | 4 | [Socket] 5 | ListenStream=@UBUS_UNIX_SOCKET@ 6 | 7 | [Install] 8 | WantedBy=sockets.target 9 | -------------------------------------------------------------------------------- /ubus_common.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 Felix Fietkau 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License version 2.1 6 | * as published by the Free Software Foundation 7 | * 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU General Public License for more details. 12 | */ 13 | 14 | #ifndef __UBUS_COMMON_H 15 | #define __UBUS_COMMON_H 16 | 17 | #define UBUS_SIGNATURE_METHOD (BLOBMSG_TYPE_LAST + 1) 18 | #define UBUS_SIGNATURE_END (BLOBMSG_TYPE_LAST + 2) 19 | 20 | #ifndef ARRAY_SIZE 21 | #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) 22 | #endif 23 | 24 | #define __init __attribute__((constructor)) 25 | 26 | #endif 27 | -------------------------------------------------------------------------------- /ubusd.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 Felix Fietkau 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License version 2.1 6 | * as published by the Free Software Foundation 7 | * 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU General Public License for more details. 12 | */ 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | #include "ubusd.h" 28 | 29 | static struct ubus_msg_buf *ubus_msg_unshare(struct ubus_msg_buf *ub) 30 | { 31 | ub = realloc(ub, sizeof(*ub) + ub->len); 32 | if (!ub) 33 | return NULL; 34 | 35 | ub->refcount = 1; 36 | memcpy(ub + 1, ub->data, ub->len); 37 | ub->data = (void *) (ub + 1); 38 | return ub; 39 | } 40 | 41 | static struct ubus_msg_buf *ubus_msg_ref(struct ubus_msg_buf *ub) 42 | { 43 | if (ub->refcount == ~0) 44 | return ubus_msg_unshare(ub); 45 | 46 | ub->refcount++; 47 | return ub; 48 | } 49 | 50 | struct ubus_msg_buf *ubus_msg_new(void *data, int len, bool shared) 51 | { 52 | struct ubus_msg_buf *ub; 53 | int buflen = sizeof(*ub); 54 | 55 | if (!shared) 56 | buflen += len; 57 | 58 | ub = calloc(1, buflen); 59 | if (!ub) 60 | return NULL; 61 | 62 | if (shared) { 63 | ub->refcount = ~0; 64 | ub->data = data; 65 | } else { 66 | ub->refcount = 1; 67 | ub->data = (void *) (ub + 1); 68 | if (data) 69 | memcpy(ub + 1, data, len); 70 | } 71 | 72 | ub->len = len; 73 | return ub; 74 | } 75 | 76 | void ubus_msg_free(struct ubus_msg_buf *ub) 77 | { 78 | switch (ub->refcount) { 79 | case 1: 80 | case ~0: 81 | free(ub); 82 | break; 83 | default: 84 | ub->refcount--; 85 | break; 86 | } 87 | } 88 | 89 | static int ubus_msg_writev(int fd, struct ubus_msg_buf *ub, int offset) 90 | { 91 | struct iovec iov[2]; 92 | 93 | if (offset < sizeof(ub->hdr)) { 94 | iov[0].iov_base = ((char *) &ub->hdr) + offset; 95 | iov[0].iov_len = sizeof(ub->hdr) - offset; 96 | iov[1].iov_base = (char *) ub->data; 97 | iov[1].iov_len = ub->len; 98 | return writev(fd, iov, 2); 99 | } else { 100 | offset -= sizeof(ub->hdr); 101 | return write(fd, ((char *) ub->data) + offset, ub->len - offset); 102 | } 103 | } 104 | 105 | static void ubus_msg_enqueue(struct ubus_client *cl, struct ubus_msg_buf *ub) 106 | { 107 | if (cl->tx_queue[cl->txq_tail]) 108 | return; 109 | 110 | cl->tx_queue[cl->txq_tail] = ubus_msg_ref(ub); 111 | cl->txq_tail = (cl->txq_tail + 1) % ARRAY_SIZE(cl->tx_queue); 112 | } 113 | 114 | /* takes the msgbuf reference */ 115 | void ubus_msg_send(struct ubus_client *cl, struct ubus_msg_buf *ub, bool free) 116 | { 117 | int written; 118 | 119 | if (!cl->tx_queue[cl->txq_cur]) { 120 | written = ubus_msg_writev(cl->sock.fd, ub, 0); 121 | if (written >= ub->len + sizeof(ub->hdr)) 122 | goto out; 123 | 124 | if (written < 0) 125 | written = 0; 126 | 127 | cl->txq_ofs = written; 128 | 129 | /* get an event once we can write to the socket again */ 130 | uloop_fd_add(&cl->sock, ULOOP_READ | ULOOP_WRITE | ULOOP_EDGE_TRIGGER); 131 | } 132 | ubus_msg_enqueue(cl, ub); 133 | 134 | out: 135 | if (free) 136 | ubus_msg_free(ub); 137 | } 138 | 139 | static struct ubus_msg_buf *ubus_msg_head(struct ubus_client *cl) 140 | { 141 | return cl->tx_queue[cl->txq_cur]; 142 | } 143 | 144 | static void ubus_msg_dequeue(struct ubus_client *cl) 145 | { 146 | struct ubus_msg_buf *ub = ubus_msg_head(cl); 147 | 148 | if (!ub) 149 | return; 150 | 151 | ubus_msg_free(ub); 152 | cl->txq_ofs = 0; 153 | cl->tx_queue[cl->txq_cur] = NULL; 154 | cl->txq_cur = (cl->txq_cur + 1) % ARRAY_SIZE(cl->tx_queue); 155 | } 156 | 157 | static void handle_client_disconnect(struct ubus_client *cl) 158 | { 159 | while (ubus_msg_head(cl)) 160 | ubus_msg_dequeue(cl); 161 | 162 | ubusd_proto_free_client(cl); 163 | uloop_fd_delete(&cl->sock); 164 | close(cl->sock.fd); 165 | free(cl); 166 | } 167 | 168 | static void client_cb(struct uloop_fd *sock, unsigned int events) 169 | { 170 | struct ubus_client *cl = container_of(sock, struct ubus_client, sock); 171 | struct ubus_msg_buf *ub; 172 | 173 | /* first try to tx more pending data */ 174 | while ((ub = ubus_msg_head(cl))) { 175 | int written; 176 | 177 | written = ubus_msg_writev(sock->fd, ub, cl->txq_ofs); 178 | if (written < 0) { 179 | switch(errno) { 180 | case EINTR: 181 | case EAGAIN: 182 | break; 183 | default: 184 | goto disconnect; 185 | } 186 | break; 187 | } 188 | 189 | cl->txq_ofs += written; 190 | if (cl->txq_ofs < ub->len + sizeof(ub->hdr)) 191 | break; 192 | 193 | ubus_msg_dequeue(cl); 194 | } 195 | 196 | /* prevent further ULOOP_WRITE events if we don't have data 197 | * to send anymore */ 198 | if (!ubus_msg_head(cl) && (events & ULOOP_WRITE)) 199 | uloop_fd_add(sock, ULOOP_READ | ULOOP_EDGE_TRIGGER); 200 | 201 | retry: 202 | if (!sock->eof && cl->pending_msg_offset < sizeof(cl->hdrbuf)) { 203 | int offset = cl->pending_msg_offset; 204 | int bytes; 205 | 206 | bytes = read(sock->fd, (char *)&cl->hdrbuf + offset, sizeof(cl->hdrbuf) - offset); 207 | if (bytes < 0) 208 | goto out; 209 | 210 | cl->pending_msg_offset += bytes; 211 | if (cl->pending_msg_offset < sizeof(cl->hdrbuf)) 212 | goto out; 213 | 214 | if (blob_pad_len(&cl->hdrbuf.data) > UBUS_MAX_MSGLEN) 215 | goto disconnect; 216 | 217 | cl->pending_msg = ubus_msg_new(NULL, blob_raw_len(&cl->hdrbuf.data), false); 218 | if (!cl->pending_msg) 219 | goto disconnect; 220 | 221 | memcpy(&cl->pending_msg->hdr, &cl->hdrbuf.hdr, sizeof(cl->hdrbuf.hdr)); 222 | memcpy(cl->pending_msg->data, &cl->hdrbuf.data, sizeof(cl->hdrbuf.data)); 223 | } 224 | 225 | ub = cl->pending_msg; 226 | if (ub) { 227 | int offset = cl->pending_msg_offset - sizeof(ub->hdr); 228 | int len = blob_raw_len(ub->data) - offset; 229 | int bytes = 0; 230 | 231 | if (len > 0) { 232 | bytes = read(sock->fd, (char *) ub->data + offset, len); 233 | if (bytes <= 0) 234 | goto out; 235 | } 236 | 237 | if (bytes < len) { 238 | cl->pending_msg_offset += bytes; 239 | goto out; 240 | } 241 | 242 | /* accept message */ 243 | cl->pending_msg_offset = 0; 244 | cl->pending_msg = NULL; 245 | ubusd_proto_receive_message(cl, ub); 246 | goto retry; 247 | } 248 | 249 | out: 250 | if (!sock->eof || ubus_msg_head(cl)) 251 | return; 252 | 253 | disconnect: 254 | handle_client_disconnect(cl); 255 | } 256 | 257 | static bool get_next_connection(int fd) 258 | { 259 | struct ubus_client *cl; 260 | int client_fd; 261 | 262 | client_fd = accept(fd, NULL, 0); 263 | if (client_fd < 0) { 264 | switch (errno) { 265 | case ECONNABORTED: 266 | case EINTR: 267 | return true; 268 | default: 269 | return false; 270 | } 271 | } 272 | 273 | cl = ubusd_proto_new_client(client_fd, client_cb); 274 | if (cl) 275 | uloop_fd_add(&cl->sock, ULOOP_READ | ULOOP_EDGE_TRIGGER); 276 | else 277 | close(client_fd); 278 | 279 | return true; 280 | } 281 | 282 | static void server_cb(struct uloop_fd *fd, unsigned int events) 283 | { 284 | bool next; 285 | 286 | do { 287 | next = get_next_connection(fd->fd); 288 | } while (next); 289 | } 290 | 291 | static struct uloop_fd server_fd = { 292 | .cb = server_cb, 293 | }; 294 | 295 | static int usage(const char *progname) 296 | { 297 | fprintf(stderr, "Usage: %s []\n" 298 | "Options: \n" 299 | " -s : Set the unix domain socket to listen on\n" 300 | "\n", progname); 301 | return 1; 302 | } 303 | 304 | int main(int argc, char **argv) 305 | { 306 | const char *ubus_socket = UBUS_UNIX_SOCKET; 307 | int ret = 0; 308 | int ch; 309 | 310 | signal(SIGPIPE, SIG_IGN); 311 | 312 | uloop_init(); 313 | 314 | while ((ch = getopt(argc, argv, "s:")) != -1) { 315 | switch (ch) { 316 | case 's': 317 | ubus_socket = optarg; 318 | break; 319 | default: 320 | return usage(argv[0]); 321 | } 322 | } 323 | 324 | unlink(ubus_socket); 325 | umask(0177); 326 | server_fd.fd = usock(USOCK_UNIX | USOCK_SERVER | USOCK_NONBLOCK, ubus_socket, NULL); 327 | if (server_fd.fd < 0) { 328 | perror("usock"); 329 | ret = -1; 330 | goto out; 331 | } 332 | uloop_fd_add(&server_fd, ULOOP_READ | ULOOP_EDGE_TRIGGER); 333 | 334 | uloop_run(); 335 | unlink(ubus_socket); 336 | 337 | out: 338 | uloop_done(); 339 | return ret; 340 | } 341 | -------------------------------------------------------------------------------- /ubusd.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 Felix Fietkau 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License version 2.1 6 | * as published by the Free Software Foundation 7 | * 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU General Public License for more details. 12 | */ 13 | 14 | #ifndef __UBUSD_H 15 | #define __UBUSD_H 16 | 17 | #include 18 | #include 19 | #include 20 | #include "ubus_common.h" 21 | #include "ubusd_id.h" 22 | #include "ubusd_obj.h" 23 | #include "ubusmsg.h" 24 | 25 | #define UBUSD_CLIENT_BACKLOG 32 26 | #define UBUS_OBJ_HASH_BITS 4 27 | 28 | extern struct blob_buf b; 29 | 30 | struct ubus_msg_buf { 31 | uint32_t refcount; /* ~0: uses external data buffer */ 32 | struct ubus_msghdr hdr; 33 | struct blob_attr *data; 34 | int len; 35 | }; 36 | 37 | struct ubus_client { 38 | struct ubus_id id; 39 | struct uloop_fd sock; 40 | 41 | struct list_head objects; 42 | 43 | struct ubus_msg_buf *tx_queue[UBUSD_CLIENT_BACKLOG]; 44 | unsigned int txq_cur, txq_tail, txq_ofs; 45 | 46 | struct ubus_msg_buf *pending_msg; 47 | int pending_msg_offset; 48 | struct { 49 | struct ubus_msghdr hdr; 50 | struct blob_attr data; 51 | } hdrbuf; 52 | }; 53 | 54 | struct ubus_path { 55 | struct list_head list; 56 | const char name[]; 57 | }; 58 | 59 | struct ubus_msg_buf *ubus_msg_new(void *data, int len, bool shared); 60 | void ubus_msg_send(struct ubus_client *cl, struct ubus_msg_buf *ub, bool free); 61 | void ubus_msg_free(struct ubus_msg_buf *ub); 62 | 63 | struct ubus_client *ubusd_proto_new_client(int fd, uloop_fd_handler cb); 64 | void ubusd_proto_receive_message(struct ubus_client *cl, struct ubus_msg_buf *ub); 65 | void ubusd_proto_free_client(struct ubus_client *cl); 66 | 67 | void ubusd_event_init(void); 68 | void ubusd_event_cleanup_object(struct ubus_object *obj); 69 | void ubusd_send_obj_event(struct ubus_object *obj, bool add); 70 | 71 | 72 | #endif 73 | -------------------------------------------------------------------------------- /ubusd_event.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 Felix Fietkau 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License version 2.1 6 | * as published by the Free Software Foundation 7 | * 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU General Public License for more details. 12 | */ 13 | 14 | #include 15 | #include "ubusd.h" 16 | 17 | static struct avl_tree patterns; 18 | static struct ubus_object *event_obj; 19 | static int event_seq = 0; 20 | static int obj_event_seq = 1; 21 | 22 | struct event_source { 23 | struct list_head list; 24 | struct ubus_object *obj; 25 | struct avl_node avl; 26 | bool partial; 27 | }; 28 | 29 | static void ubusd_delete_event_source(struct event_source *evs) 30 | { 31 | list_del(&evs->list); 32 | avl_delete(&patterns, &evs->avl); 33 | free(evs); 34 | } 35 | 36 | void ubusd_event_cleanup_object(struct ubus_object *obj) 37 | { 38 | struct event_source *ev; 39 | 40 | while (!list_empty(&obj->events)) { 41 | ev = list_first_entry(&obj->events, struct event_source, list); 42 | ubusd_delete_event_source(ev); 43 | } 44 | } 45 | 46 | enum { 47 | EVREG_PATTERN, 48 | EVREG_OBJECT, 49 | EVREG_LAST, 50 | }; 51 | 52 | static struct blobmsg_policy evr_policy[] = { 53 | [EVREG_PATTERN] = { .name = "pattern", .type = BLOBMSG_TYPE_STRING }, 54 | [EVREG_OBJECT] = { .name = "object", .type = BLOBMSG_TYPE_INT32 }, 55 | }; 56 | 57 | static int ubusd_alloc_event_pattern(struct ubus_client *cl, struct blob_attr *msg) 58 | { 59 | struct event_source *ev; 60 | struct ubus_object *obj; 61 | struct blob_attr *attr[EVREG_LAST]; 62 | char *pattern, *name; 63 | uint32_t id; 64 | bool partial = false; 65 | int len; 66 | 67 | blobmsg_parse(evr_policy, EVREG_LAST, attr, blob_data(msg), blob_len(msg)); 68 | if (!attr[EVREG_OBJECT] || !attr[EVREG_PATTERN]) 69 | return UBUS_STATUS_INVALID_ARGUMENT; 70 | 71 | id = blobmsg_get_u32(attr[EVREG_OBJECT]); 72 | if (id < UBUS_SYSTEM_OBJECT_MAX) 73 | return UBUS_STATUS_PERMISSION_DENIED; 74 | 75 | obj = ubusd_find_object(id); 76 | if (!obj) 77 | return UBUS_STATUS_NOT_FOUND; 78 | 79 | if (obj->client != cl) 80 | return UBUS_STATUS_PERMISSION_DENIED; 81 | 82 | pattern = blobmsg_data(attr[EVREG_PATTERN]); 83 | 84 | len = strlen(pattern); 85 | if (pattern[len - 1] == '*') { 86 | partial = true; 87 | pattern[len - 1] = 0; 88 | len--; 89 | } 90 | 91 | ev = calloc(1, sizeof(*ev) + len + 1); 92 | if (!ev) 93 | return UBUS_STATUS_NO_DATA; 94 | 95 | list_add(&ev->list, &obj->events); 96 | ev->obj = obj; 97 | ev->partial = partial; 98 | name = (char *) (ev + 1); 99 | strcpy(name, pattern); 100 | ev->avl.key = name; 101 | avl_insert(&patterns, &ev->avl); 102 | 103 | return 0; 104 | } 105 | 106 | typedef struct ubus_msg_buf *(*event_fill_cb)(void *priv, const char *id); 107 | 108 | static void ubusd_send_event_msg(struct ubus_msg_buf **ub, struct ubus_client *cl, 109 | struct ubus_object *obj, const char *id, 110 | event_fill_cb fill_cb, void *cb_priv) 111 | { 112 | uint32_t *objid_ptr; 113 | 114 | /* do not loop back events */ 115 | if (obj->client == cl) 116 | return; 117 | 118 | /* do not send duplicate events */ 119 | if (obj->event_seen == obj_event_seq) 120 | return; 121 | 122 | obj->event_seen = obj_event_seq; 123 | 124 | if (!*ub) { 125 | *ub = fill_cb(cb_priv, id); 126 | (*ub)->hdr.type = UBUS_MSG_INVOKE; 127 | (*ub)->hdr.peer = 0; 128 | } 129 | 130 | objid_ptr = blob_data(blob_data((*ub)->data)); 131 | *objid_ptr = htonl(obj->id.id); 132 | 133 | (*ub)->hdr.seq = ++event_seq; 134 | ubus_msg_send(obj->client, *ub, false); 135 | } 136 | 137 | static bool strmatch_len(const char *s1, const char *s2, int *len) 138 | { 139 | for (*len = 0; s1[*len] == s2[*len]; (*len)++) 140 | if (!s1[*len]) 141 | return true; 142 | 143 | return false; 144 | } 145 | 146 | static int ubusd_send_event(struct ubus_client *cl, const char *id, 147 | event_fill_cb fill_cb, void *cb_priv) 148 | { 149 | struct ubus_msg_buf *ub = NULL; 150 | struct event_source *ev; 151 | int match_len = 0; 152 | 153 | obj_event_seq++; 154 | 155 | /* 156 | * Since this tree is sorted alphabetically, we can only expect to find 157 | * matching entries as long as the number of matching characters 158 | * between the pattern string and our string is monotonically increasing. 159 | */ 160 | avl_for_each_element(&patterns, ev, avl) { 161 | const char *key = ev->avl.key; 162 | int cur_match_len; 163 | bool full_match; 164 | 165 | full_match = strmatch_len(id, key, &cur_match_len); 166 | if (cur_match_len < match_len) 167 | break; 168 | 169 | match_len = cur_match_len; 170 | 171 | if (!full_match) { 172 | if (!ev->partial) 173 | continue; 174 | 175 | if (match_len != strlen(key)) 176 | continue; 177 | } 178 | 179 | ubusd_send_event_msg(&ub, cl, ev->obj, id, fill_cb, cb_priv); 180 | } 181 | 182 | if (ub) 183 | ubus_msg_free(ub); 184 | 185 | return 0; 186 | } 187 | 188 | enum { 189 | EVMSG_ID, 190 | EVMSG_DATA, 191 | EVMSG_LAST, 192 | }; 193 | 194 | static struct blobmsg_policy ev_policy[] = { 195 | [EVMSG_ID] = { .name = "id", .type = BLOBMSG_TYPE_STRING }, 196 | [EVMSG_DATA] = { .name = "data", .type = BLOBMSG_TYPE_TABLE }, 197 | }; 198 | 199 | static struct ubus_msg_buf * 200 | ubusd_create_event_from_msg(void *priv, const char *id) 201 | { 202 | struct blob_attr *msg = priv; 203 | 204 | blob_buf_init(&b, 0); 205 | blob_put_int32(&b, UBUS_ATTR_OBJID, 0); 206 | blob_put_string(&b, UBUS_ATTR_METHOD, id); 207 | blob_put(&b, UBUS_ATTR_DATA, blobmsg_data(msg), blobmsg_data_len(msg)); 208 | 209 | return ubus_msg_new(b.head, blob_raw_len(b.head), true); 210 | } 211 | 212 | static int ubusd_forward_event(struct ubus_client *cl, struct blob_attr *msg) 213 | { 214 | struct blob_attr *data; 215 | struct blob_attr *attr[EVMSG_LAST]; 216 | const char *id; 217 | 218 | blobmsg_parse(ev_policy, EVMSG_LAST, attr, blob_data(msg), blob_len(msg)); 219 | if (!attr[EVMSG_ID] || !attr[EVMSG_DATA]) 220 | return UBUS_STATUS_INVALID_ARGUMENT; 221 | 222 | id = blobmsg_data(attr[EVMSG_ID]); 223 | data = attr[EVMSG_DATA]; 224 | 225 | if (!strncmp(id, "ubus.", 5)) 226 | return UBUS_STATUS_PERMISSION_DENIED; 227 | 228 | return ubusd_send_event(cl, id, ubusd_create_event_from_msg, data); 229 | } 230 | 231 | static int ubusd_event_recv(struct ubus_client *cl, const char *method, struct blob_attr *msg) 232 | { 233 | if (!strcmp(method, "register")) 234 | return ubusd_alloc_event_pattern(cl, msg); 235 | 236 | if (!strcmp(method, "send")) 237 | return ubusd_forward_event(cl, msg); 238 | 239 | return UBUS_STATUS_INVALID_COMMAND; 240 | } 241 | 242 | static struct ubus_msg_buf * 243 | ubusd_create_object_event_msg(void *priv, const char *id) 244 | { 245 | struct ubus_object *obj = priv; 246 | void *s; 247 | 248 | blob_buf_init(&b, 0); 249 | blob_put_int32(&b, UBUS_ATTR_OBJID, 0); 250 | blob_put_string(&b, UBUS_ATTR_METHOD, id); 251 | s = blob_nest_start(&b, UBUS_ATTR_DATA); 252 | blobmsg_add_u32(&b, "id", obj->id.id); 253 | blobmsg_add_string(&b, "path", obj->path.key); 254 | blob_nest_end(&b, s); 255 | 256 | return ubus_msg_new(b.head, blob_raw_len(b.head), true); 257 | } 258 | 259 | void ubusd_send_obj_event(struct ubus_object *obj, bool add) 260 | { 261 | const char *id = add ? "ubus.object.add" : "ubus.object.remove"; 262 | 263 | ubusd_send_event(NULL, id, ubusd_create_object_event_msg, obj); 264 | } 265 | 266 | void ubusd_event_init(void) 267 | { 268 | ubus_init_string_tree(&patterns, true); 269 | event_obj = ubusd_create_object_internal(NULL, UBUS_SYSTEM_OBJECT_EVENT); 270 | event_obj->recv_msg = ubusd_event_recv; 271 | } 272 | 273 | -------------------------------------------------------------------------------- /ubusd_id.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 Felix Fietkau 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License version 2.1 6 | * as published by the Free Software Foundation 7 | * 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU General Public License for more details. 12 | */ 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | #include "ubusmsg.h" 21 | #include "ubusd_id.h" 22 | 23 | static int random_fd = -1; 24 | 25 | static int ubus_cmp_id(const void *k1, const void *k2, void *ptr) 26 | { 27 | const uint32_t *id1 = k1, *id2 = k2; 28 | 29 | if (*id1 < *id2) 30 | return -1; 31 | else 32 | return *id1 > *id2; 33 | } 34 | 35 | void ubus_init_string_tree(struct avl_tree *tree, bool dup) 36 | { 37 | avl_init(tree, avl_strcmp, dup, NULL); 38 | } 39 | 40 | void ubus_init_id_tree(struct avl_tree *tree) 41 | { 42 | if (random_fd < 0) { 43 | random_fd = open("/dev/urandom", O_RDONLY); 44 | if (random_fd < 0) { 45 | perror("open"); 46 | exit(1); 47 | } 48 | } 49 | 50 | avl_init(tree, ubus_cmp_id, false, NULL); 51 | } 52 | 53 | bool ubus_alloc_id(struct avl_tree *tree, struct ubus_id *id, uint32_t val) 54 | { 55 | id->avl.key = &id->id; 56 | if (val) { 57 | id->id = val; 58 | return avl_insert(tree, &id->avl) == 0; 59 | } 60 | 61 | do { 62 | if (read(random_fd, &id->id, sizeof(id->id)) != sizeof(id->id)) 63 | return false; 64 | 65 | if (id->id < UBUS_SYSTEM_OBJECT_MAX) 66 | continue; 67 | } while (avl_insert(tree, &id->avl) != 0); 68 | 69 | return true; 70 | } 71 | 72 | -------------------------------------------------------------------------------- /ubusd_id.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 Felix Fietkau 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License version 2.1 6 | * as published by the Free Software Foundation 7 | * 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU General Public License for more details. 12 | */ 13 | 14 | #ifndef __UBUSD_ID_H 15 | #define __UBUSD_ID_H 16 | 17 | #include 18 | #include 19 | 20 | struct ubus_id { 21 | struct avl_node avl; 22 | uint32_t id; 23 | }; 24 | 25 | void ubus_init_id_tree(struct avl_tree *tree); 26 | void ubus_init_string_tree(struct avl_tree *tree, bool dup); 27 | bool ubus_alloc_id(struct avl_tree *tree, struct ubus_id *id, uint32_t val); 28 | 29 | static inline void ubus_free_id(struct avl_tree *tree, struct ubus_id *id) 30 | { 31 | avl_delete(tree, &id->avl); 32 | } 33 | 34 | static inline struct ubus_id *ubus_find_id(struct avl_tree *tree, uint32_t id) 35 | { 36 | struct avl_node *avl; 37 | 38 | avl = avl_find(tree, &id); 39 | if (!avl) 40 | return NULL; 41 | 42 | return container_of(avl, struct ubus_id, avl); 43 | } 44 | 45 | #endif 46 | -------------------------------------------------------------------------------- /ubusd_obj.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 Felix Fietkau 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License version 2.1 6 | * as published by the Free Software Foundation 7 | * 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU General Public License for more details. 12 | */ 13 | 14 | #include "ubusd.h" 15 | #include "ubusd_obj.h" 16 | 17 | struct avl_tree obj_types; 18 | struct avl_tree objects; 19 | struct avl_tree path; 20 | 21 | static void ubus_unref_object_type(struct ubus_object_type *type) 22 | { 23 | struct ubus_method *m; 24 | 25 | if (--type->refcount > 0) 26 | return; 27 | 28 | while (!list_empty(&type->methods)) { 29 | m = list_first_entry(&type->methods, struct ubus_method, list); 30 | list_del(&m->list); 31 | free(m); 32 | } 33 | 34 | ubus_free_id(&obj_types, &type->id); 35 | free(type); 36 | } 37 | 38 | static bool ubus_create_obj_method(struct ubus_object_type *type, struct blob_attr *attr) 39 | { 40 | struct ubus_method *m; 41 | int bloblen = blob_raw_len(attr); 42 | 43 | m = calloc(1, sizeof(*m) + bloblen); 44 | if (!m) 45 | return false; 46 | 47 | list_add_tail(&m->list, &type->methods); 48 | memcpy(m->data, attr, bloblen); 49 | m->name = blobmsg_name(m->data); 50 | 51 | return true; 52 | } 53 | 54 | static struct ubus_object_type *ubus_create_obj_type(struct blob_attr *sig) 55 | { 56 | struct ubus_object_type *type; 57 | struct blob_attr *pos; 58 | int rem; 59 | 60 | type = calloc(1, sizeof(*type)); 61 | type->refcount = 1; 62 | 63 | if (!ubus_alloc_id(&obj_types, &type->id, 0)) 64 | goto error_free; 65 | 66 | INIT_LIST_HEAD(&type->methods); 67 | 68 | blob_for_each_attr(pos, sig, rem) { 69 | if (!blobmsg_check_attr(pos, true)) 70 | goto error_unref; 71 | 72 | if (!ubus_create_obj_method(type, pos)) 73 | goto error_unref; 74 | } 75 | 76 | return type; 77 | 78 | error_unref: 79 | ubus_unref_object_type(type); 80 | return NULL; 81 | 82 | error_free: 83 | free(type); 84 | return NULL; 85 | } 86 | 87 | static struct ubus_object_type *ubus_get_obj_type(uint32_t obj_id) 88 | { 89 | struct ubus_object_type *type; 90 | struct ubus_id *id; 91 | 92 | id = ubus_find_id(&obj_types, obj_id); 93 | if (!id) 94 | return NULL; 95 | 96 | type = container_of(id, struct ubus_object_type, id); 97 | type->refcount++; 98 | return type; 99 | } 100 | 101 | struct ubus_object *ubusd_create_object_internal(struct ubus_object_type *type, uint32_t id) 102 | { 103 | struct ubus_object *obj; 104 | 105 | obj = calloc(1, sizeof(*obj)); 106 | if (!obj) 107 | return NULL; 108 | 109 | if (!ubus_alloc_id(&objects, &obj->id, id)) 110 | goto error_free; 111 | 112 | obj->type = type; 113 | INIT_LIST_HEAD(&obj->list); 114 | INIT_LIST_HEAD(&obj->events); 115 | INIT_LIST_HEAD(&obj->subscribers); 116 | INIT_LIST_HEAD(&obj->target_list); 117 | if (type) 118 | type->refcount++; 119 | 120 | return obj; 121 | 122 | error_free: 123 | free(obj); 124 | return NULL; 125 | } 126 | 127 | struct ubus_object *ubusd_create_object(struct ubus_client *cl, struct blob_attr **attr) 128 | { 129 | struct ubus_object *obj; 130 | struct ubus_object_type *type = NULL; 131 | 132 | if (attr[UBUS_ATTR_OBJTYPE]) 133 | type = ubus_get_obj_type(blob_get_u32(attr[UBUS_ATTR_OBJTYPE])); 134 | else if (attr[UBUS_ATTR_SIGNATURE]) 135 | type = ubus_create_obj_type(attr[UBUS_ATTR_SIGNATURE]); 136 | 137 | obj = ubusd_create_object_internal(type, 0); 138 | if (type) 139 | ubus_unref_object_type(type); 140 | 141 | if (!obj) 142 | return NULL; 143 | 144 | if (attr[UBUS_ATTR_OBJPATH]) { 145 | obj->path.key = strdup(blob_data(attr[UBUS_ATTR_OBJPATH])); 146 | if (!obj->path.key) 147 | goto free; 148 | 149 | if (avl_insert(&path, &obj->path) != 0) { 150 | free((void *) obj->path.key); 151 | obj->path.key = NULL; 152 | goto free; 153 | } 154 | ubusd_send_obj_event(obj, true); 155 | } 156 | 157 | obj->client = cl; 158 | list_add(&obj->list, &cl->objects); 159 | 160 | return obj; 161 | 162 | free: 163 | ubusd_free_object(obj); 164 | return NULL; 165 | } 166 | 167 | void ubus_subscribe(struct ubus_object *obj, struct ubus_object *target) 168 | { 169 | struct ubus_subscription *s; 170 | bool first = list_empty(&target->subscribers); 171 | 172 | s = calloc(1, sizeof(*s)); 173 | if (!s) 174 | return; 175 | 176 | s->subscriber = obj; 177 | s->target = target; 178 | list_add(&s->list, &target->subscribers); 179 | list_add(&s->target_list, &obj->target_list); 180 | 181 | if (first) 182 | ubus_notify_subscription(target); 183 | } 184 | 185 | void ubus_unsubscribe(struct ubus_subscription *s) 186 | { 187 | struct ubus_object *obj = s->target; 188 | 189 | list_del(&s->list); 190 | list_del(&s->target_list); 191 | free(s); 192 | 193 | if (list_empty(&obj->subscribers)) 194 | ubus_notify_subscription(obj); 195 | } 196 | 197 | void ubusd_free_object(struct ubus_object *obj) 198 | { 199 | struct ubus_subscription *s, *tmp; 200 | 201 | list_for_each_entry_safe(s, tmp, &obj->target_list, target_list) { 202 | ubus_unsubscribe(s); 203 | } 204 | list_for_each_entry_safe(s, tmp, &obj->subscribers, list) { 205 | ubus_notify_unsubscribe(s); 206 | } 207 | 208 | ubusd_event_cleanup_object(obj); 209 | if (obj->path.key) { 210 | ubusd_send_obj_event(obj, false); 211 | avl_delete(&path, &obj->path); 212 | free((void *) obj->path.key); 213 | } 214 | if (!list_empty(&obj->list)) 215 | list_del(&obj->list); 216 | ubus_free_id(&objects, &obj->id); 217 | if (obj->type) 218 | ubus_unref_object_type(obj->type); 219 | free(obj); 220 | } 221 | 222 | static void __init ubusd_obj_init(void) 223 | { 224 | ubus_init_id_tree(&objects); 225 | ubus_init_id_tree(&obj_types); 226 | ubus_init_string_tree(&path, false); 227 | ubusd_event_init(); 228 | } 229 | -------------------------------------------------------------------------------- /ubusd_obj.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 Felix Fietkau 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License version 2.1 6 | * as published by the Free Software Foundation 7 | * 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU General Public License for more details. 12 | */ 13 | 14 | #ifndef __UBUSD_OBJ_H 15 | #define __UBUSD_OBJ_H 16 | 17 | #include "ubusd_id.h" 18 | 19 | extern struct avl_tree obj_types; 20 | extern struct avl_tree objects; 21 | extern struct avl_tree path; 22 | 23 | struct ubus_client; 24 | struct ubus_msg_buf; 25 | 26 | struct ubus_object_type { 27 | struct ubus_id id; 28 | int refcount; 29 | struct list_head methods; 30 | }; 31 | 32 | struct ubus_method { 33 | struct list_head list; 34 | const char *name; 35 | struct blob_attr data[]; 36 | }; 37 | 38 | struct ubus_subscription { 39 | struct list_head list, target_list; 40 | struct ubus_object *subscriber, *target; 41 | }; 42 | 43 | struct ubus_object { 44 | struct ubus_id id; 45 | struct list_head list; 46 | 47 | struct list_head events; 48 | 49 | struct list_head subscribers, target_list; 50 | 51 | struct ubus_object_type *type; 52 | struct avl_node path; 53 | 54 | struct ubus_client *client; 55 | int (*recv_msg)(struct ubus_client *client, const char *method, struct blob_attr *msg); 56 | 57 | int event_seen; 58 | unsigned int invoke_seq; 59 | }; 60 | 61 | struct ubus_object *ubusd_create_object(struct ubus_client *cl, struct blob_attr **attr); 62 | struct ubus_object *ubusd_create_object_internal(struct ubus_object_type *type, uint32_t id); 63 | void ubusd_free_object(struct ubus_object *obj); 64 | 65 | static inline struct ubus_object *ubusd_find_object(uint32_t objid) 66 | { 67 | struct ubus_object *obj; 68 | struct ubus_id *id; 69 | 70 | id = ubus_find_id(&objects, objid); 71 | if (!id) 72 | return NULL; 73 | 74 | obj = container_of(id, struct ubus_object, id); 75 | return obj; 76 | } 77 | 78 | void ubus_subscribe(struct ubus_object *obj, struct ubus_object *target); 79 | void ubus_unsubscribe(struct ubus_subscription *s); 80 | void ubus_notify_unsubscribe(struct ubus_subscription *s); 81 | void ubus_notify_subscription(struct ubus_object *obj); 82 | 83 | #endif 84 | -------------------------------------------------------------------------------- /ubusd_proto.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 Felix Fietkau 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License version 2.1 6 | * as published by the Free Software Foundation 7 | * 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU General Public License for more details. 12 | */ 13 | 14 | #include 15 | #include "ubusd.h" 16 | 17 | struct blob_buf b; 18 | static struct ubus_msg_buf *retmsg; 19 | static int *retmsg_data; 20 | static struct avl_tree clients; 21 | 22 | static struct blob_attr *attrbuf[UBUS_ATTR_MAX]; 23 | 24 | typedef int (*ubus_cmd_cb)(struct ubus_client *cl, struct ubus_msg_buf *ub, struct blob_attr **attr); 25 | 26 | static const struct blob_attr_info ubus_policy[UBUS_ATTR_MAX] = { 27 | [UBUS_ATTR_SIGNATURE] = { .type = BLOB_ATTR_NESTED }, 28 | [UBUS_ATTR_OBJTYPE] = { .type = BLOB_ATTR_INT32 }, 29 | [UBUS_ATTR_OBJPATH] = { .type = BLOB_ATTR_STRING }, 30 | [UBUS_ATTR_OBJID] = { .type = BLOB_ATTR_INT32 }, 31 | [UBUS_ATTR_STATUS] = { .type = BLOB_ATTR_INT32 }, 32 | [UBUS_ATTR_METHOD] = { .type = BLOB_ATTR_STRING }, 33 | }; 34 | 35 | static struct blob_attr **ubus_parse_msg(struct blob_attr *msg) 36 | { 37 | blob_parse(msg, attrbuf, ubus_policy, UBUS_ATTR_MAX); 38 | return attrbuf; 39 | } 40 | 41 | static void ubus_msg_init(struct ubus_msg_buf *ub, uint8_t type, uint16_t seq, uint32_t peer) 42 | { 43 | ub->hdr.version = 0; 44 | ub->hdr.type = type; 45 | ub->hdr.seq = seq; 46 | ub->hdr.peer = peer; 47 | } 48 | 49 | static struct ubus_msg_buf *ubus_msg_from_blob(bool shared) 50 | { 51 | return ubus_msg_new(b.head, blob_raw_len(b.head), shared); 52 | } 53 | 54 | static struct ubus_msg_buf *ubus_reply_from_blob(struct ubus_msg_buf *ub, bool shared) 55 | { 56 | struct ubus_msg_buf *new; 57 | 58 | new = ubus_msg_from_blob(shared); 59 | if (!new) 60 | return NULL; 61 | 62 | ubus_msg_init(new, UBUS_MSG_DATA, ub->hdr.seq, ub->hdr.peer); 63 | return new; 64 | } 65 | 66 | static void 67 | ubus_send_msg_from_blob(struct ubus_client *cl, struct ubus_msg_buf *ub, 68 | uint8_t type) 69 | { 70 | ub = ubus_reply_from_blob(ub, true); 71 | if (!ub) 72 | return; 73 | 74 | ub->hdr.type = type; 75 | ubus_msg_send(cl, ub, true); 76 | } 77 | 78 | static bool ubusd_send_hello(struct ubus_client *cl) 79 | { 80 | struct ubus_msg_buf *ub; 81 | 82 | blob_buf_init(&b, 0); 83 | ub = ubus_msg_from_blob(true); 84 | if (!ub) 85 | return false; 86 | 87 | ubus_msg_init(ub, UBUS_MSG_HELLO, 0, cl->id.id); 88 | ubus_msg_send(cl, ub, true); 89 | return true; 90 | } 91 | 92 | static int ubusd_send_pong(struct ubus_client *cl, struct ubus_msg_buf *ub, struct blob_attr **attr) 93 | { 94 | ub->hdr.type = UBUS_MSG_DATA; 95 | ubus_msg_send(cl, ub, false); 96 | return 0; 97 | } 98 | 99 | static int ubusd_handle_remove_object(struct ubus_client *cl, struct ubus_msg_buf *ub, struct blob_attr **attr) 100 | { 101 | struct ubus_object *obj; 102 | 103 | if (!attr[UBUS_ATTR_OBJID]) 104 | return UBUS_STATUS_INVALID_ARGUMENT; 105 | 106 | obj = ubusd_find_object(blob_get_u32(attr[UBUS_ATTR_OBJID])); 107 | if (!obj) 108 | return UBUS_STATUS_NOT_FOUND; 109 | 110 | if (obj->client != cl) 111 | return UBUS_STATUS_PERMISSION_DENIED; 112 | 113 | blob_buf_init(&b, 0); 114 | blob_put_int32(&b, UBUS_ATTR_OBJID, obj->id.id); 115 | 116 | /* check if we're removing the object type as well */ 117 | if (obj->type && obj->type->refcount == 1) 118 | blob_put_int32(&b, UBUS_ATTR_OBJTYPE, obj->type->id.id); 119 | 120 | ubusd_free_object(obj); 121 | ubus_send_msg_from_blob(cl, ub, UBUS_MSG_DATA); 122 | 123 | return 0; 124 | } 125 | 126 | static int ubusd_handle_add_object(struct ubus_client *cl, struct ubus_msg_buf *ub, struct blob_attr **attr) 127 | { 128 | struct ubus_object *obj; 129 | 130 | obj = ubusd_create_object(cl, attr); 131 | if (!obj) 132 | return UBUS_STATUS_INVALID_ARGUMENT; 133 | 134 | blob_buf_init(&b, 0); 135 | blob_put_int32(&b, UBUS_ATTR_OBJID, obj->id.id); 136 | if (attr[UBUS_ATTR_SIGNATURE]) 137 | blob_put_int32(&b, UBUS_ATTR_OBJTYPE, obj->type->id.id); 138 | 139 | ubus_send_msg_from_blob(cl, ub, UBUS_MSG_DATA); 140 | return 0; 141 | } 142 | 143 | static void ubusd_send_obj(struct ubus_client *cl, struct ubus_msg_buf *ub, struct ubus_object *obj) 144 | { 145 | struct ubus_method *m; 146 | void *s; 147 | 148 | blob_buf_init(&b, 0); 149 | 150 | if (obj->path.key) 151 | blob_put_string(&b, UBUS_ATTR_OBJPATH, obj->path.key); 152 | blob_put_int32(&b, UBUS_ATTR_OBJID, obj->id.id); 153 | blob_put_int32(&b, UBUS_ATTR_OBJTYPE, obj->type->id.id); 154 | 155 | s = blob_nest_start(&b, UBUS_ATTR_SIGNATURE); 156 | list_for_each_entry(m, &obj->type->methods, list) 157 | blob_put(&b, blob_id(m->data), blob_data(m->data), blob_len(m->data)); 158 | blob_nest_end(&b, s); 159 | 160 | ubus_send_msg_from_blob(cl, ub, UBUS_MSG_DATA); 161 | } 162 | 163 | static int ubusd_handle_lookup(struct ubus_client *cl, struct ubus_msg_buf *ub, struct blob_attr **attr) 164 | { 165 | struct ubus_object *obj; 166 | char *objpath; 167 | bool found = false; 168 | int len; 169 | 170 | if (!attr[UBUS_ATTR_OBJPATH]) { 171 | avl_for_each_element(&path, obj, path) 172 | ubusd_send_obj(cl, ub, obj); 173 | return 0; 174 | } 175 | 176 | objpath = blob_data(attr[UBUS_ATTR_OBJPATH]); 177 | len = strlen(objpath); 178 | if (objpath[len - 1] != '*') { 179 | obj = avl_find_element(&path, objpath, obj, path); 180 | if (!obj) 181 | return UBUS_STATUS_NOT_FOUND; 182 | 183 | ubusd_send_obj(cl, ub, obj); 184 | return 0; 185 | } 186 | 187 | objpath[--len] = 0; 188 | 189 | obj = avl_find_ge_element(&path, objpath, obj, path); 190 | if (!obj) 191 | return UBUS_STATUS_NOT_FOUND; 192 | 193 | while (!strncmp(objpath, obj->path.key, len)) { 194 | found = true; 195 | ubusd_send_obj(cl, ub, obj); 196 | if (obj == avl_last_element(&path, obj, path)) 197 | break; 198 | obj = avl_next_element(obj, path); 199 | } 200 | 201 | if (!found) 202 | return UBUS_STATUS_NOT_FOUND; 203 | 204 | return 0; 205 | } 206 | 207 | static void 208 | ubusd_forward_invoke(struct ubus_object *obj, const char *method, 209 | struct ubus_msg_buf *ub, struct blob_attr *data) 210 | { 211 | blob_put_int32(&b, UBUS_ATTR_OBJID, obj->id.id); 212 | blob_put_string(&b, UBUS_ATTR_METHOD, method); 213 | if (data) 214 | blob_put(&b, UBUS_ATTR_DATA, blob_data(data), blob_len(data)); 215 | 216 | ubus_send_msg_from_blob(obj->client, ub, UBUS_MSG_INVOKE); 217 | } 218 | 219 | static int ubusd_handle_invoke(struct ubus_client *cl, struct ubus_msg_buf *ub, struct blob_attr **attr) 220 | { 221 | struct ubus_object *obj = NULL; 222 | struct ubus_id *id; 223 | const char *method; 224 | 225 | if (!attr[UBUS_ATTR_METHOD] || !attr[UBUS_ATTR_OBJID]) 226 | return UBUS_STATUS_INVALID_ARGUMENT; 227 | 228 | id = ubus_find_id(&objects, blob_get_u32(attr[UBUS_ATTR_OBJID])); 229 | if (!id) 230 | return UBUS_STATUS_NOT_FOUND; 231 | 232 | obj = container_of(id, struct ubus_object, id); 233 | 234 | method = blob_data(attr[UBUS_ATTR_METHOD]); 235 | 236 | if (!obj->client) 237 | return obj->recv_msg(cl, method, attr[UBUS_ATTR_DATA]); 238 | 239 | ub->hdr.peer = cl->id.id; 240 | blob_buf_init(&b, 0); 241 | ubusd_forward_invoke(obj, method, ub, attr[UBUS_ATTR_DATA]); 242 | ubus_msg_free(ub); 243 | 244 | return -1; 245 | } 246 | 247 | static int ubusd_handle_notify(struct ubus_client *cl, struct ubus_msg_buf *ub, struct blob_attr **attr) 248 | { 249 | struct ubus_object *obj = NULL; 250 | struct ubus_subscription *s; 251 | struct ubus_id *id; 252 | const char *method; 253 | bool no_reply = false; 254 | void *c; 255 | 256 | if (!attr[UBUS_ATTR_METHOD] || !attr[UBUS_ATTR_OBJID]) 257 | return UBUS_STATUS_INVALID_ARGUMENT; 258 | 259 | if (attr[UBUS_ATTR_NO_REPLY]) 260 | no_reply = blob_get_int8(attr[UBUS_ATTR_NO_REPLY]); 261 | 262 | id = ubus_find_id(&objects, blob_get_u32(attr[UBUS_ATTR_OBJID])); 263 | if (!id) 264 | return UBUS_STATUS_NOT_FOUND; 265 | 266 | obj = container_of(id, struct ubus_object, id); 267 | if (obj->client != cl) 268 | return UBUS_STATUS_PERMISSION_DENIED; 269 | 270 | if (!no_reply) { 271 | blob_buf_init(&b, 0); 272 | blob_put_int32(&b, UBUS_ATTR_OBJID, id->id); 273 | c = blob_nest_start(&b, UBUS_ATTR_SUBSCRIBERS); 274 | list_for_each_entry(s, &obj->subscribers, list) { 275 | blob_put_int32(&b, 0, s->subscriber->id.id); 276 | } 277 | blob_nest_end(&b, c); 278 | blob_put_int32(&b, UBUS_ATTR_STATUS, 0); 279 | ubus_send_msg_from_blob(cl, ub, UBUS_MSG_STATUS); 280 | } 281 | 282 | ub->hdr.peer = cl->id.id; 283 | method = blob_data(attr[UBUS_ATTR_METHOD]); 284 | list_for_each_entry(s, &obj->subscribers, list) { 285 | blob_buf_init(&b, 0); 286 | if (no_reply) 287 | blob_put_int8(&b, UBUS_ATTR_NO_REPLY, 1); 288 | ubusd_forward_invoke(s->subscriber, method, ub, attr[UBUS_ATTR_DATA]); 289 | } 290 | ubus_msg_free(ub); 291 | 292 | return -1; 293 | } 294 | 295 | static struct ubus_client *ubusd_get_client_by_id(uint32_t id) 296 | { 297 | struct ubus_id *clid; 298 | 299 | clid = ubus_find_id(&clients, id); 300 | if (!clid) 301 | return NULL; 302 | 303 | return container_of(clid, struct ubus_client, id); 304 | } 305 | 306 | static int ubusd_handle_response(struct ubus_client *cl, struct ubus_msg_buf *ub, struct blob_attr **attr) 307 | { 308 | struct ubus_object *obj; 309 | 310 | if (!attr[UBUS_ATTR_OBJID] || 311 | (ub->hdr.type == UBUS_MSG_STATUS && !attr[UBUS_ATTR_STATUS]) || 312 | (ub->hdr.type == UBUS_MSG_DATA && !attr[UBUS_ATTR_DATA])) 313 | goto error; 314 | 315 | obj = ubusd_find_object(blob_get_u32(attr[UBUS_ATTR_OBJID])); 316 | if (!obj) 317 | goto error; 318 | 319 | if (cl != obj->client) 320 | goto error; 321 | 322 | cl = ubusd_get_client_by_id(ub->hdr.peer); 323 | if (!cl) 324 | goto error; 325 | 326 | ub->hdr.peer = blob_get_u32(attr[UBUS_ATTR_OBJID]); 327 | ubus_msg_send(cl, ub, true); 328 | return -1; 329 | 330 | error: 331 | ubus_msg_free(ub); 332 | return -1; 333 | } 334 | 335 | static int ubusd_handle_add_watch(struct ubus_client *cl, struct ubus_msg_buf *ub, struct blob_attr **attr) 336 | { 337 | struct ubus_object *obj, *target; 338 | 339 | if (!attr[UBUS_ATTR_OBJID] || !attr[UBUS_ATTR_TARGET]) 340 | return UBUS_STATUS_INVALID_ARGUMENT; 341 | 342 | obj = ubusd_find_object(blob_get_u32(attr[UBUS_ATTR_OBJID])); 343 | if (!obj) 344 | return UBUS_STATUS_NOT_FOUND; 345 | 346 | if (cl != obj->client) 347 | return UBUS_STATUS_INVALID_ARGUMENT; 348 | 349 | target = ubusd_find_object(blob_get_u32(attr[UBUS_ATTR_TARGET])); 350 | if (!target) 351 | return UBUS_STATUS_NOT_FOUND; 352 | 353 | if (cl == target->client) 354 | return UBUS_STATUS_INVALID_ARGUMENT; 355 | 356 | ubus_subscribe(obj, target); 357 | return 0; 358 | } 359 | 360 | static int ubusd_handle_remove_watch(struct ubus_client *cl, struct ubus_msg_buf *ub, struct blob_attr **attr) 361 | { 362 | struct ubus_object *obj; 363 | struct ubus_subscription *s; 364 | uint32_t id; 365 | 366 | if (!attr[UBUS_ATTR_OBJID] || !attr[UBUS_ATTR_TARGET]) 367 | return UBUS_STATUS_INVALID_ARGUMENT; 368 | 369 | obj = ubusd_find_object(blob_get_u32(attr[UBUS_ATTR_OBJID])); 370 | if (!obj) 371 | return UBUS_STATUS_NOT_FOUND; 372 | 373 | if (cl != obj->client) 374 | return UBUS_STATUS_INVALID_ARGUMENT; 375 | 376 | id = blob_get_u32(attr[UBUS_ATTR_TARGET]); 377 | list_for_each_entry(s, &obj->target_list, target_list) { 378 | if (s->target->id.id != id) 379 | continue; 380 | 381 | ubus_unsubscribe(s); 382 | return 0; 383 | } 384 | 385 | return UBUS_STATUS_NOT_FOUND; 386 | } 387 | 388 | static const ubus_cmd_cb handlers[__UBUS_MSG_LAST] = { 389 | [UBUS_MSG_PING] = ubusd_send_pong, 390 | [UBUS_MSG_ADD_OBJECT] = ubusd_handle_add_object, 391 | [UBUS_MSG_REMOVE_OBJECT] = ubusd_handle_remove_object, 392 | [UBUS_MSG_LOOKUP] = ubusd_handle_lookup, 393 | [UBUS_MSG_INVOKE] = ubusd_handle_invoke, 394 | [UBUS_MSG_STATUS] = ubusd_handle_response, 395 | [UBUS_MSG_DATA] = ubusd_handle_response, 396 | [UBUS_MSG_SUBSCRIBE] = ubusd_handle_add_watch, 397 | [UBUS_MSG_UNSUBSCRIBE] = ubusd_handle_remove_watch, 398 | [UBUS_MSG_NOTIFY] = ubusd_handle_notify, 399 | }; 400 | 401 | void ubusd_proto_receive_message(struct ubus_client *cl, struct ubus_msg_buf *ub) 402 | { 403 | ubus_cmd_cb cb = NULL; 404 | int ret; 405 | 406 | retmsg->hdr.seq = ub->hdr.seq; 407 | retmsg->hdr.peer = ub->hdr.peer; 408 | 409 | if (ub->hdr.type < __UBUS_MSG_LAST) 410 | cb = handlers[ub->hdr.type]; 411 | 412 | if (cb) 413 | ret = cb(cl, ub, ubus_parse_msg(ub->data)); 414 | else 415 | ret = UBUS_STATUS_INVALID_COMMAND; 416 | 417 | if (ret == -1) 418 | return; 419 | 420 | ubus_msg_free(ub); 421 | 422 | *retmsg_data = htonl(ret); 423 | ubus_msg_send(cl, retmsg, false); 424 | } 425 | 426 | struct ubus_client *ubusd_proto_new_client(int fd, uloop_fd_handler cb) 427 | { 428 | struct ubus_client *cl; 429 | 430 | cl = calloc(1, sizeof(*cl)); 431 | if (!cl) 432 | return NULL; 433 | 434 | INIT_LIST_HEAD(&cl->objects); 435 | cl->sock.fd = fd; 436 | cl->sock.cb = cb; 437 | 438 | if (!ubus_alloc_id(&clients, &cl->id, 0)) 439 | goto free; 440 | 441 | if (!ubusd_send_hello(cl)) 442 | goto delete; 443 | 444 | return cl; 445 | 446 | delete: 447 | ubus_free_id(&clients, &cl->id); 448 | free: 449 | free(cl); 450 | return NULL; 451 | } 452 | 453 | void ubusd_proto_free_client(struct ubus_client *cl) 454 | { 455 | struct ubus_object *obj; 456 | 457 | while (!list_empty(&cl->objects)) { 458 | obj = list_first_entry(&cl->objects, struct ubus_object, list); 459 | ubusd_free_object(obj); 460 | } 461 | 462 | ubus_free_id(&clients, &cl->id); 463 | } 464 | 465 | void ubus_notify_subscription(struct ubus_object *obj) 466 | { 467 | bool active = !list_empty(&obj->subscribers); 468 | struct ubus_msg_buf *ub; 469 | 470 | blob_buf_init(&b, 0); 471 | blob_put_int32(&b, UBUS_ATTR_OBJID, obj->id.id); 472 | blob_put_int8(&b, UBUS_ATTR_ACTIVE, active); 473 | 474 | ub = ubus_msg_from_blob(false); 475 | ubus_msg_init(ub, UBUS_MSG_NOTIFY, ++obj->invoke_seq, 0); 476 | ubus_msg_send(obj->client, ub, true); 477 | } 478 | 479 | void ubus_notify_unsubscribe(struct ubus_subscription *s) 480 | { 481 | struct ubus_msg_buf *ub; 482 | 483 | blob_buf_init(&b, 0); 484 | blob_put_int32(&b, UBUS_ATTR_OBJID, s->subscriber->id.id); 485 | blob_put_int32(&b, UBUS_ATTR_TARGET, s->target->id.id); 486 | 487 | ub = ubus_msg_from_blob(false); 488 | ubus_msg_init(ub, UBUS_MSG_UNSUBSCRIBE, ++s->subscriber->invoke_seq, 0); 489 | ubus_msg_send(s->subscriber->client, ub, true); 490 | 491 | ubus_unsubscribe(s); 492 | } 493 | 494 | static void __init ubusd_proto_init(void) 495 | { 496 | ubus_init_id_tree(&clients); 497 | 498 | blob_buf_init(&b, 0); 499 | blob_put_int32(&b, UBUS_ATTR_STATUS, 0); 500 | 501 | retmsg = ubus_msg_from_blob(false); 502 | if (!retmsg) 503 | exit(1); 504 | 505 | retmsg->hdr.type = UBUS_MSG_STATUS; 506 | retmsg_data = blob_data(blob_data(retmsg->data)); 507 | } 508 | -------------------------------------------------------------------------------- /ubusmsg.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 Felix Fietkau 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License version 2.1 6 | * as published by the Free Software Foundation 7 | * 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU General Public License for more details. 12 | */ 13 | 14 | #ifndef __UBUSMSG_H 15 | #define __UBUSMSG_H 16 | 17 | #include 18 | #include 19 | 20 | #define __packetdata __attribute__((packed)) __attribute__((__aligned__(4))) 21 | 22 | #define UBUS_MAX_MSGLEN 65536 23 | 24 | #define UBUS_SYSTEM_OBJECT_EVENT 1 25 | #define UBUS_SYSTEM_OBJECT_MAX 1024 26 | 27 | struct ubus_msghdr { 28 | uint8_t version; 29 | uint8_t type; 30 | uint16_t seq; 31 | uint32_t peer; 32 | } __packetdata; 33 | 34 | enum ubus_msg_type { 35 | /* initial server message */ 36 | UBUS_MSG_HELLO, 37 | 38 | /* generic command response */ 39 | UBUS_MSG_STATUS, 40 | 41 | /* data message response */ 42 | UBUS_MSG_DATA, 43 | 44 | /* ping request */ 45 | UBUS_MSG_PING, 46 | 47 | /* look up one or more objects */ 48 | UBUS_MSG_LOOKUP, 49 | 50 | /* invoke a method on a single object */ 51 | UBUS_MSG_INVOKE, 52 | 53 | UBUS_MSG_ADD_OBJECT, 54 | UBUS_MSG_REMOVE_OBJECT, 55 | 56 | /* 57 | * subscribe/unsubscribe to object notifications 58 | * The unsubscribe message is sent from ubusd when 59 | * the object disappears 60 | */ 61 | UBUS_MSG_SUBSCRIBE, 62 | UBUS_MSG_UNSUBSCRIBE, 63 | 64 | /* 65 | * send a notification to all subscribers of an object. 66 | * when sent from the server, it indicates a subscription 67 | * status change 68 | */ 69 | UBUS_MSG_NOTIFY, 70 | 71 | /* must be last */ 72 | __UBUS_MSG_LAST, 73 | }; 74 | 75 | enum ubus_msg_attr { 76 | UBUS_ATTR_UNSPEC, 77 | 78 | UBUS_ATTR_STATUS, 79 | 80 | UBUS_ATTR_OBJPATH, 81 | UBUS_ATTR_OBJID, 82 | UBUS_ATTR_METHOD, 83 | 84 | UBUS_ATTR_OBJTYPE, 85 | UBUS_ATTR_SIGNATURE, 86 | 87 | UBUS_ATTR_DATA, 88 | UBUS_ATTR_TARGET, 89 | 90 | UBUS_ATTR_ACTIVE, 91 | UBUS_ATTR_NO_REPLY, 92 | 93 | UBUS_ATTR_SUBSCRIBERS, 94 | 95 | /* must be last */ 96 | UBUS_ATTR_MAX, 97 | }; 98 | 99 | enum ubus_msg_status { 100 | UBUS_STATUS_OK, 101 | UBUS_STATUS_INVALID_COMMAND, 102 | UBUS_STATUS_INVALID_ARGUMENT, 103 | UBUS_STATUS_METHOD_NOT_FOUND, 104 | UBUS_STATUS_NOT_FOUND, 105 | UBUS_STATUS_NO_DATA, 106 | UBUS_STATUS_PERMISSION_DENIED, 107 | UBUS_STATUS_TIMEOUT, 108 | UBUS_STATUS_NOT_SUPPORTED, 109 | UBUS_STATUS_UNKNOWN_ERROR, 110 | UBUS_STATUS_CONNECTION_FAILED, 111 | __UBUS_STATUS_LAST 112 | }; 113 | 114 | #endif 115 | --------------------------------------------------------------------------------