123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550 |
- /*
- * rpcd - UBUS RPC server
- *
- * Copyright (C) 2013-2014 Jo-Philipp Wich <jow@openwrt.org>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
- #include <rpcd/plugin.h>
- static struct blob_buf buf;
- struct rpc_plugin_lookup_context {
- uint32_t id;
- char *name;
- bool found;
- };
- static void
- rpc_plugin_lookup_plugin_cb(struct ubus_context *ctx,
- struct ubus_object_data *obj, void *priv)
- {
- struct rpc_plugin_lookup_context *c = priv;
- if (c->id == obj->id)
- {
- c->found = true;
- sprintf(c->name, "%s", obj->path);
- }
- }
- static bool
- rpc_plugin_lookup_plugin(struct ubus_context *ctx, struct ubus_object *obj,
- char *strptr)
- {
- struct rpc_plugin_lookup_context c = { .id = obj->id, .name = strptr };
- if (ubus_lookup(ctx, NULL, rpc_plugin_lookup_plugin_cb, &c))
- return false;
- return c.found;
- }
- static void
- rpc_plugin_json_array_to_blob(struct array_list *a, struct blob_buf *blob);
- static void
- rpc_plugin_json_object_to_blob(json_object *o, struct blob_buf *blob);
- static void
- rpc_plugin_json_element_to_blob(const char *name, json_object *val,
- struct blob_buf *blob)
- {
- void *c;
- int64_t n;
- switch (json_object_get_type(val)) {
- case json_type_object:
- c = blobmsg_open_table(blob, name);
- rpc_plugin_json_object_to_blob(val, blob);
- blobmsg_close_table(blob, c);
- break;
- case json_type_array:
- c = blobmsg_open_array(blob, name);
- rpc_plugin_json_array_to_blob(json_object_get_array(val), blob);
- blobmsg_close_array(blob, c);
- break;
- case json_type_string:
- blobmsg_add_string(blob, name, json_object_get_string(val));
- break;
- case json_type_boolean:
- blobmsg_add_u8(blob, name, json_object_get_boolean(val));
- break;
- case json_type_int:
- n = json_object_get_int64(val);
- if (n >= INT32_MIN && n <= INT32_MAX)
- blobmsg_add_u32(blob, name, n);
- else
- blobmsg_add_u64(blob, name, n);
- break;
- case json_type_double:
- blobmsg_add_double(blob, name, json_object_get_double(val));
- break;
- case json_type_null:
- blobmsg_add_field(blob, BLOBMSG_TYPE_UNSPEC, name, NULL, 0);
- break;
- }
- }
- static void
- rpc_plugin_json_array_to_blob(struct array_list *a, struct blob_buf *blob)
- {
- int i, len;
- for (i = 0, len = array_list_length(a); i < len; i++)
- rpc_plugin_json_element_to_blob(NULL, array_list_get_idx(a, i), blob);
- }
- static void
- rpc_plugin_json_object_to_blob(json_object *o, struct blob_buf *blob)
- {
- json_object_object_foreach(o, key, val)
- rpc_plugin_json_element_to_blob(key, val, blob);
- }
- struct call_context {
- char path[PATH_MAX];
- const char *argv[4];
- char *method;
- char *input;
- json_tokener *tok;
- json_object *obj;
- bool input_done;
- bool output_done;
- };
- static int
- rpc_plugin_call_stdin_cb(struct ustream *s, void *priv)
- {
- struct call_context *c = priv;
- if (!c->input_done)
- {
- ustream_write(s, c->input, strlen(c->input), false);
- c->input_done = true;
- }
- return 0;
- }
- static int
- rpc_plugin_call_stdout_cb(struct blob_buf *blob, char *buf, int len, void *priv)
- {
- struct call_context *c = priv;
- if (!c->output_done)
- {
- c->obj = json_tokener_parse_ex(c->tok, buf, len);
- if (json_tokener_get_error(c->tok) != json_tokener_continue)
- c->output_done = true;
- }
- return len;
- }
- static int
- rpc_plugin_call_stderr_cb(struct blob_buf *blob, char *buf, int len, void *priv)
- {
- return len;
- }
- static int
- rpc_plugin_call_finish_cb(struct blob_buf *blob, int stat, void *priv)
- {
- struct call_context *c = priv;
- int rv = UBUS_STATUS_INVALID_ARGUMENT;
- if (json_tokener_get_error(c->tok) == json_tokener_success)
- {
- if (c->obj)
- {
- if (json_object_get_type(c->obj) == json_type_object)
- {
- rpc_plugin_json_object_to_blob(c->obj, blob);
- rv = UBUS_STATUS_OK;
- }
- json_object_put(c->obj);
- }
- else
- {
- rv = UBUS_STATUS_NO_DATA;
- }
- }
- json_tokener_free(c->tok);
- free(c->input);
- return rv;
- }
- static int
- rpc_plugin_call(struct ubus_context *ctx, struct ubus_object *obj,
- struct ubus_request_data *req, const char *method,
- struct blob_attr *msg)
- {
- int rv = UBUS_STATUS_UNKNOWN_ERROR;
- struct call_context *c;
- char *plugin, *mptr;
- c = calloc_a(sizeof(*c), &mptr, strlen(method) + 1);
- if (!c)
- goto fail;
- c->method = strcpy(mptr, method);
- c->input = blobmsg_format_json(msg, true);
- c->tok = json_tokener_new();
- if (!c->input || !c->tok)
- goto fail;
- plugin = c->path + sprintf(c->path, "%s/", RPC_PLUGIN_DIRECTORY);
- if (!rpc_plugin_lookup_plugin(ctx, obj, plugin))
- {
- rv = UBUS_STATUS_NOT_FOUND;
- goto fail;
- }
- c->argv[0] = c->path;
- c->argv[1] = "call";
- c->argv[2] = c->method;
- rv = rpc_exec(c->argv, rpc_plugin_call_stdin_cb,
- rpc_plugin_call_stdout_cb, rpc_plugin_call_stderr_cb,
- rpc_plugin_call_finish_cb, c, ctx, req);
- if (rv == UBUS_STATUS_OK)
- return rv;
- fail:
- if (c)
- {
- if (c->input)
- free(c->input);
- if (c->tok)
- json_tokener_free(c->tok);
- free(c);
- }
- return rv;
- }
- static bool
- rpc_plugin_parse_signature(struct blob_attr *sig, struct ubus_method *method)
- {
- int rem, n_attr;
- enum blobmsg_type type;
- struct blob_attr *attr;
- struct blobmsg_policy *policy = NULL;
- if (!sig || blobmsg_type(sig) != BLOBMSG_TYPE_TABLE)
- return false;
- n_attr = 0;
- blobmsg_for_each_attr(attr, sig, rem)
- n_attr++;
- if (n_attr)
- {
- policy = calloc(n_attr, sizeof(*policy));
- if (!policy)
- return false;
- n_attr = 0;
- blobmsg_for_each_attr(attr, sig, rem)
- {
- type = blobmsg_type(attr);
- if (type == BLOBMSG_TYPE_INT32)
- {
- switch (blobmsg_get_u32(attr))
- {
- case 8:
- type = BLOBMSG_TYPE_INT8;
- break;
- case 16:
- type = BLOBMSG_TYPE_INT16;
- break;
- case 64:
- type = BLOBMSG_TYPE_INT64;
- break;
- default:
- type = BLOBMSG_TYPE_INT32;
- break;
- }
- }
- policy[n_attr].name = strdup(blobmsg_name(attr));
- policy[n_attr].type = type;
- n_attr++;
- }
- }
- method->name = strdup(blobmsg_name(sig));
- method->handler = rpc_plugin_call;
- method->policy = policy;
- method->n_policy = n_attr;
- return true;
- }
- static struct ubus_object *
- rpc_plugin_parse_exec(const char *name, int fd)
- {
- int len, rem, n_method;
- struct blob_attr *cur;
- struct ubus_method *methods;
- struct ubus_object_type *obj_type;
- struct ubus_object *obj;
- char outbuf[1024];
- json_tokener *tok;
- json_object *jsobj;
- blob_buf_init(&buf, 0);
- tok = json_tokener_new();
- if (!tok)
- return NULL;
- while ((len = read(fd, outbuf, sizeof(outbuf))) > 0)
- {
- jsobj = json_tokener_parse_ex(tok, outbuf, len);
- if (json_tokener_get_error(tok) == json_tokener_continue)
- continue;
- if (json_tokener_get_error(tok) != json_tokener_success)
- break;
- if (jsobj)
- {
- if (json_object_get_type(jsobj) == json_type_object)
- blobmsg_add_object(&buf, jsobj);
- json_object_put(jsobj);
- break;
- }
- }
- json_tokener_free(tok);
- n_method = 0;
- blob_for_each_attr(cur, buf.head, rem)
- n_method++;
- if (!n_method)
- return NULL;
- methods = calloc(n_method, sizeof(*methods));
- if (!methods)
- return NULL;
- n_method = 0;
- blob_for_each_attr(cur, buf.head, rem)
- {
- if (!rpc_plugin_parse_signature(cur, &methods[n_method]))
- continue;
- n_method++;
- }
- obj = calloc(1, sizeof(*obj));
- if (!obj)
- return NULL;
- obj_type = calloc(1, sizeof(*obj_type));
- if (!obj_type) {
- free(obj);
- return NULL;
- }
- if (asprintf((char **)&obj_type->name, "rpcd-plugin-exec-%s", name) < 0) {
- free(obj);
- free(obj_type);
- return NULL;
- }
- obj_type->methods = methods;
- obj_type->n_methods = n_method;
- obj->name = strdup(name);
- obj->type = obj_type;
- obj->methods = methods;
- obj->n_methods = n_method;
- return obj;
- }
- static int
- rpc_plugin_register_exec(struct ubus_context *ctx, const char *path)
- {
- pid_t pid;
- int rv = UBUS_STATUS_NO_DATA, fd, fds[2];
- const char *name;
- struct ubus_object *plugin;
- name = strrchr(path, '/');
- if (!name)
- return UBUS_STATUS_INVALID_ARGUMENT;
- if (pipe(fds))
- return UBUS_STATUS_UNKNOWN_ERROR;
- switch ((pid = fork()))
- {
- case -1:
- return UBUS_STATUS_UNKNOWN_ERROR;
- case 0:
- fd = open("/dev/null", O_RDWR);
- if (fd > -1)
- {
- dup2(fd, 0);
- dup2(fd, 2);
- if (fd > 2)
- close(fd);
- }
- dup2(fds[1], 1);
- close(fds[0]);
- close(fds[1]);
- if (execl(path, path, "list", NULL))
- return UBUS_STATUS_UNKNOWN_ERROR;
- default:
- plugin = rpc_plugin_parse_exec(name + 1, fds[0]);
- if (!plugin)
- goto out;
- rv = ubus_add_object(ctx, plugin);
- out:
- close(fds[0]);
- close(fds[1]);
- waitpid(pid, NULL, 0);
- return rv;
- }
- }
- static LIST_HEAD(plugins);
- static const struct rpc_daemon_ops ops = {
- .session_access = rpc_session_access,
- .session_create_cb = rpc_session_create_cb,
- .session_destroy_cb = rpc_session_destroy_cb,
- .exec = rpc_exec,
- .exec_timeout = &rpc_exec_timeout,
- };
- static int
- rpc_plugin_register_library(struct ubus_context *ctx, const char *path)
- {
- struct rpc_plugin *p;
- void *dlh;
- dlh = dlopen(path, RTLD_LAZY | RTLD_LOCAL);
- if (!dlh) {
- fprintf(stderr, "Failed to load plugin %s: %s\n",
- path, dlerror());
- return UBUS_STATUS_UNKNOWN_ERROR;
- }
- p = dlsym(dlh, "rpc_plugin");
- if (!p)
- return UBUS_STATUS_NOT_FOUND;
- list_add(&p->list, &plugins);
- return p->init(&ops, ctx);
- }
- int rpc_plugin_api_init(struct ubus_context *ctx)
- {
- DIR *d;
- int rv = 0;
- struct stat s;
- struct dirent *e;
- char path[PATH_MAX];
- if ((d = opendir(RPC_PLUGIN_DIRECTORY)) != NULL)
- {
- while ((e = readdir(d)) != NULL)
- {
- snprintf(path, sizeof(path) - 1,
- RPC_PLUGIN_DIRECTORY "/%s", e->d_name);
- if (stat(path, &s) || !S_ISREG(s.st_mode) || !(s.st_mode & S_IXUSR))
- continue;
- rv |= rpc_plugin_register_exec(ctx, path);
- }
- closedir(d);
- }
- if ((d = opendir(RPC_LIBRARY_DIRECTORY)) != NULL)
- {
- while ((e = readdir(d)) != NULL)
- {
- snprintf(path, sizeof(path) - 1,
- RPC_LIBRARY_DIRECTORY "/%s", e->d_name);
- if (stat(path, &s) || !S_ISREG(s.st_mode))
- continue;
- rv |= rpc_plugin_register_library(ctx, path);
- }
- closedir(d);
- }
- return rv;
- }
|