*/
#include "plugin.h"
+#include "exec.h"
static struct blob_buf buf;
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 };
+ struct call_context *c = priv;
- meth = strdup(method);
- input = blobmsg_format_json(msg, true);
- plugin = path + sprintf(path, "%s/", RPC_PLUGIN_DIRECTORY);
-
- 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_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 ||
+ json_object_get_type(c->obj) == json_type_array)
+ {
+ blobmsg_add_json_element(blob, NULL, 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;
+
+ return rpc_exec(c->argv, rpc_plugin_call_stdin_cb,
+ rpc_plugin_call_stdout_cb, NULL, rpc_plugin_call_finish_cb,
+ c, ctx, req);
- if (!rv)
- ubus_send_reply(ctx, req, buf.head);
+fail:
+ if (c)
+ {
+ if (c->method)
+ free(c->method);
- free(meth);
+ if (c->input)
+ free(c->input);
- return rv;
+ if (c->tok)
+ json_tokener_free(c->tok);
+
+ free(c);
}
+
+ return rv;
}
static bool
}
static struct ubus_object *
-rpc_plugin_parse_plugin(const char *name, const char *listbuf)
+rpc_plugin_parse_plugin(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)
rpc_plugin_register(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, '/');
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_plugin(name + 1, fds[0]);
if (!plugin)
goto out;