X-Git-Url: http://git.archive.openwrt.org/?p=project%2Frpcd.git;a=blobdiff_plain;f=plugin.c;h=b75241aae1f82ce0a84b5d40c1c74b2e8c0577ed;hp=26dfda478a51b29abaa7065746aaf5819e589635;hb=b65f6a844f920eb19f7d05d2ee93819b72021057;hpb=a86ef267a4fb8ce72477a916952bc69ff68d63d4 diff --git a/plugin.c b/plugin.c index 26dfda4..b75241a 100644 --- a/plugin.c +++ b/plugin.c @@ -1,7 +1,7 @@ /* - * luci-rpcd - LuCI UBUS RPC server + * rpcd - UBUS RPC server * - * Copyright (C) 2013 Jo-Philipp Wich + * Copyright (C) 2013-2014 Jo-Philipp Wich * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -16,7 +16,7 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include "plugin.h" +#include static struct blob_buf buf; @@ -51,92 +51,136 @@ rpc_plugin_lookup_plugin(struct ubus_context *ctx, struct ubus_object *obj, return c.found; } +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(struct ubus_context *ctx, struct ubus_object *obj, - struct ubus_request_data *req, const char *method, - struct blob_attr *msg) +rpc_plugin_call_stdin_cb(struct ustream *s, void *priv) { - pid_t pid; - struct stat s; - int rv, fd, in_fds[2], out_fds[2]; - char *input, *plugin, *meth, output[4096] = { 0 }, path[PATH_MAX] = { 0 }; - - meth = strdup(method); - input = blobmsg_format_json(msg, true); - plugin = path + sprintf(path, "%s/", RPC_PLUGIN_DIRECTORY); + struct call_context *c = priv; - if (!rpc_plugin_lookup_plugin(ctx, obj, plugin)) - return UBUS_STATUS_NOT_FOUND; + if (!c->input_done) + { + ustream_write(s, c->input, strlen(c->input), false); + c->input_done = true; + } - if (stat(path, &s) || !(s.st_mode & S_IXUSR)) - return UBUS_STATUS_NOT_FOUND; + return 0; +} - if (pipe(in_fds) || pipe(out_fds)) - return UBUS_STATUS_UNKNOWN_ERROR; +static int +rpc_plugin_call_stdout_cb(struct blob_buf *blob, char *buf, int len, void *priv) +{ + struct call_context *c = priv; - switch ((pid = fork())) + if (!c->output_done) { - case -1: - return UBUS_STATUS_UNKNOWN_ERROR; + c->obj = json_tokener_parse_ex(c->tok, buf, len); - case 0: - uloop_done(); + if (json_tokener_get_error(c->tok) != json_tokener_continue) + c->output_done = true; + } - fd = open("/dev/null", O_RDWR); + return len; +} - if (fd > -1) +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) { - dup2(fd, 2); + if (json_object_get_type(c->obj) == json_type_object && + blobmsg_add_object(blob, c->obj)) + rv = UBUS_STATUS_OK; - if (fd > 2) - close(fd); + json_object_put(c->obj); } + else + { + rv = UBUS_STATUS_NO_DATA; + } + } - dup2(in_fds[0], 0); - dup2(out_fds[1], 1); + json_tokener_free(c->tok); - close(in_fds[0]); - close(in_fds[1]); - close(out_fds[0]); - close(out_fds[1]); + free(c->input); + free(c->method); - if (execl(path, plugin, "call", meth, NULL)) - return UBUS_STATUS_UNKNOWN_ERROR; + return rv; +} - default: - rv = UBUS_STATUS_NO_DATA; +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; - if (input) - { - write(in_fds[1], input, strlen(input)); - free(input); - } + c = calloc(1, sizeof(*c)); - close(in_fds[0]); - close(in_fds[1]); + if (!c) + goto fail; - if (read(out_fds[0], output, sizeof(output) - 1) > 0) - { - blob_buf_init(&buf, 0); + c->method = strdup(method); + c->input = blobmsg_format_json(msg, true); + c->tok = json_tokener_new(); - if (!blobmsg_add_json_from_string(&buf, output)) - rv = UBUS_STATUS_INVALID_ARGUMENT; + if (!c->method || !c->input || !c->tok) + goto fail; - rv = UBUS_STATUS_OK; - } + plugin = c->path + sprintf(c->path, "%s/", RPC_PLUGIN_DIRECTORY); - close(out_fds[0]); - close(out_fds[1]); + if (!rpc_plugin_lookup_plugin(ctx, obj, plugin)) + { + rv = UBUS_STATUS_NOT_FOUND; + goto fail; + } - waitpid(pid, NULL, 0); + c->argv[0] = c->path; + c->argv[1] = "call"; + c->argv[2] = c->method; - if (!rv) - ubus_send_reply(ctx, req, buf.head); + return 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); - free(meth); +fail: + if (c) + { + if (c->method) + free(c->method); - return rv; + if (c->input) + free(c->input); + + if (c->tok) + json_tokener_free(c->tok); + + free(c); } + + return rv; } static bool @@ -147,7 +191,7 @@ rpc_plugin_parse_signature(struct blob_attr *sig, struct ubus_method *method) struct blob_attr *attr; struct blobmsg_policy *policy = NULL; - if (!sig || blob_id(sig) != BLOBMSG_TYPE_TABLE) + if (!sig || blobmsg_type(sig) != BLOBMSG_TYPE_TABLE) return false; n_attr = 0; @@ -166,7 +210,7 @@ rpc_plugin_parse_signature(struct blob_attr *sig, struct ubus_method *method) blobmsg_for_each_attr(attr, sig, rem) { - type = blob_id(attr); + type = blobmsg_type(attr); if (type == BLOBMSG_TYPE_INT32) { @@ -206,19 +250,47 @@ rpc_plugin_parse_signature(struct blob_attr *sig, struct ubus_method *method) } static struct ubus_object * -rpc_plugin_parse_plugin(const char *name, const char *listbuf) +rpc_plugin_parse_exec(const char *name, int fd) { - int rem, n_method; + 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); - if (!blobmsg_add_json_from_string(&buf, listbuf)) + 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) @@ -265,12 +337,11 @@ rpc_plugin_parse_plugin(const char *name, const char *listbuf) } static int -rpc_plugin_register(struct ubus_context *ctx, const char *path) +rpc_plugin_register_exec(struct ubus_context *ctx, const char *path) { pid_t pid; - int rv, fd, fds[2]; + int rv = UBUS_STATUS_NO_DATA, fd, fds[2]; const char *name; - char listbuf[4096] = { 0 }; struct ubus_object *plugin; name = strrchr(path, '/'); @@ -307,12 +378,7 @@ rpc_plugin_register(struct ubus_context *ctx, const char *path) return UBUS_STATUS_UNKNOWN_ERROR; default: - rv = 0; - - if (read(fds[0], listbuf, sizeof(listbuf) - 1) <= 0) - goto out; - - plugin = rpc_plugin_parse_plugin(name + 1, listbuf); + plugin = rpc_plugin_parse_exec(name + 1, fds[0]); if (!plugin) goto out; @@ -328,6 +394,37 @@ out: } } + +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, +}; + +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_GLOBAL); + + if (!dlh) + 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; @@ -336,22 +433,37 @@ int rpc_plugin_api_init(struct ubus_context *ctx) struct dirent *e; char path[PATH_MAX]; - d = opendir(RPC_PLUGIN_DIRECTORY); + 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 (!d) - return UBUS_STATUS_NOT_FOUND; + if (stat(path, &s) || !S_ISREG(s.st_mode) || !(s.st_mode & S_IXUSR)) + continue; - while ((e = readdir(d)) != NULL) + rv |= rpc_plugin_register_exec(ctx, path); + } + + closedir(d); + } + + if ((d = opendir(RPC_LIBRARY_DIRECTORY)) != NULL) { - snprintf(path, sizeof(path) - 1, RPC_PLUGIN_DIRECTORY "/%s", e->d_name); + 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) || !(s.st_mode & S_IXUSR)) - continue; + if (stat(path, &s) || !S_ISREG(s.st_mode)) + continue; - rv |= rpc_plugin_register(ctx, path); - } + rv |= rpc_plugin_register_library(ctx, path); + } - closedir(d); + closedir(d); + } return rv; }