Link json-c
[project/rpcd.git] / plugin.c
1 /*
2  * luci-rpcd - LuCI UBUS RPC server
3  *
4  *   Copyright (C) 2013 Jo-Philipp Wich <jow@openwrt.org>
5  *
6  * Permission to use, copy, modify, and/or distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18
19 #include "plugin.h"
20 #include "exec.h"
21
22 static struct blob_buf buf;
23
24 struct rpc_plugin_lookup_context {
25         uint32_t id;
26         char *name;
27         bool found;
28 };
29
30 static void
31 rpc_plugin_lookup_plugin_cb(struct ubus_context *ctx,
32                             struct ubus_object_data *obj, void *priv)
33 {
34         struct rpc_plugin_lookup_context *c = priv;
35
36         if (c->id == obj->id)
37         {
38                 c->found = true;
39                 sprintf(c->name, "%s", obj->path);
40         }
41 }
42
43 static bool
44 rpc_plugin_lookup_plugin(struct ubus_context *ctx, struct ubus_object *obj,
45                          char *strptr)
46 {
47         struct rpc_plugin_lookup_context c = { .id = obj->id, .name = strptr };
48
49         if (ubus_lookup(ctx, NULL, rpc_plugin_lookup_plugin_cb, &c))
50                 return false;
51
52         return c.found;
53 }
54
55 struct call_context {
56         char path[PATH_MAX];
57         const char *argv[4];
58         char *method;
59         char *input;
60         json_tokener *tok;
61         json_object *obj;
62         bool input_done;
63         bool output_done;
64 };
65
66 static int
67 rpc_plugin_call_stdin_cb(struct ustream *s, void *priv)
68 {
69         struct call_context *c = priv;
70
71         if (!c->input_done)
72         {
73                 ustream_write(s, c->input, strlen(c->input), false);
74                 c->input_done = true;
75         }
76
77         return 0;
78 }
79
80 static int
81 rpc_plugin_call_stdout_cb(struct blob_buf *blob, char *buf, int len, void *priv)
82 {
83         struct call_context *c = priv;
84
85         if (!c->output_done)
86         {
87                 c->obj = json_tokener_parse_ex(c->tok, buf, len);
88
89                 if (json_tokener_get_error(c->tok) != json_tokener_continue)
90                         c->output_done = true;
91         }
92
93         return len;
94 }
95
96 static int
97 rpc_plugin_call_finish_cb(struct blob_buf *blob, int stat, void *priv)
98 {
99         struct call_context *c = priv;
100         int rv = UBUS_STATUS_INVALID_ARGUMENT;
101
102         if (json_tokener_get_error(c->tok) == json_tokener_success)
103         {
104                 if (c->obj)
105                 {
106                         if (json_object_get_type(c->obj) == json_type_object ||
107                             json_object_get_type(c->obj) == json_type_array)
108                         {
109                                 blobmsg_add_json_element(blob, NULL, c->obj);
110                                 rv = UBUS_STATUS_OK;
111                         }
112
113                         json_object_put(c->obj);
114                 }
115                 else
116                 {
117                         rv = UBUS_STATUS_NO_DATA;
118                 }
119         }
120
121         json_tokener_free(c->tok);
122
123         free(c->input);
124         free(c->method);
125
126         return rv;
127 }
128
129 static int
130 rpc_plugin_call(struct ubus_context *ctx, struct ubus_object *obj,
131                 struct ubus_request_data *req, const char *method,
132                 struct blob_attr *msg)
133 {
134         int rv = UBUS_STATUS_UNKNOWN_ERROR;
135         struct call_context *c;
136         char *plugin;
137
138         c = calloc(1, sizeof(*c));
139
140         if (!c)
141                 goto fail;
142
143         c->method = strdup(method);
144         c->input = blobmsg_format_json(msg, true);
145         c->tok = json_tokener_new();
146
147         if (!c->method || !c->input || !c->tok)
148                 goto fail;
149
150         plugin = c->path + sprintf(c->path, "%s/", RPC_PLUGIN_DIRECTORY);
151
152         if (!rpc_plugin_lookup_plugin(ctx, obj, plugin))
153         {
154                 rv = UBUS_STATUS_NOT_FOUND;
155                 goto fail;
156         }
157
158         c->argv[0] = c->path;
159         c->argv[1] = "call";
160         c->argv[2] = c->method;
161
162         return rpc_exec(c->argv, rpc_plugin_call_stdin_cb,
163                         rpc_plugin_call_stdout_cb, NULL, rpc_plugin_call_finish_cb,
164                         c, ctx, req);
165
166 fail:
167         if (c)
168         {
169                 if (c->method)
170                         free(c->method);
171
172                 if (c->input)
173                         free(c->input);
174
175                 if (c->tok)
176                         json_tokener_free(c->tok);
177
178                 free(c);
179         }
180
181         return rv;
182 }
183
184 static bool
185 rpc_plugin_parse_signature(struct blob_attr *sig, struct ubus_method *method)
186 {
187         int rem, n_attr;
188         enum blobmsg_type type;
189         struct blob_attr *attr;
190         struct blobmsg_policy *policy = NULL;
191
192         if (!sig || blob_id(sig) != BLOBMSG_TYPE_TABLE)
193                 return false;
194
195         n_attr = 0;
196
197         blobmsg_for_each_attr(attr, sig, rem)
198                 n_attr++;
199
200         if (n_attr)
201         {
202                 policy = calloc(n_attr, sizeof(*policy));
203
204                 if (!policy)
205                         return false;
206
207                 n_attr = 0;
208
209                 blobmsg_for_each_attr(attr, sig, rem)
210                 {
211                         type = blob_id(attr);
212
213                         if (type == BLOBMSG_TYPE_INT32)
214                         {
215                                 switch (blobmsg_get_u32(attr))
216                                 {
217                                 case 8:
218                                         type = BLOBMSG_TYPE_INT8;
219                                         break;
220
221                                 case 16:
222                                         type = BLOBMSG_TYPE_INT16;
223                                         break;
224
225                                 case 64:
226                                         type = BLOBMSG_TYPE_INT64;
227                                         break;
228
229                                 default:
230                                         type = BLOBMSG_TYPE_INT32;
231                                         break;
232                                 }
233                         }
234
235                         policy[n_attr].name = strdup(blobmsg_name(attr));
236                         policy[n_attr].type = type;
237
238                         n_attr++;
239                 }
240         }
241
242         method->name = strdup(blobmsg_name(sig));
243         method->handler = rpc_plugin_call;
244         method->policy = policy;
245         method->n_policy = n_attr;
246
247         return true;
248 }
249
250 static struct ubus_object *
251 rpc_plugin_parse_plugin(const char *name, int fd)
252 {
253         int len, rem, n_method;
254         struct blob_attr *cur;
255         struct ubus_method *methods;
256         struct ubus_object_type *obj_type;
257         struct ubus_object *obj;
258         char outbuf[1024];
259
260         json_tokener *tok;
261         json_object *jsobj;
262
263         blob_buf_init(&buf, 0);
264
265         tok = json_tokener_new();
266
267         if (!tok)
268                 return NULL;
269
270         while ((len = read(fd, outbuf, sizeof(outbuf))) > 0)
271         {
272                 jsobj = json_tokener_parse_ex(tok, outbuf, len);
273
274                 if (json_tokener_get_error(tok) == json_tokener_continue)
275                         continue;
276
277                 if (json_tokener_get_error(tok) != json_tokener_success)
278                         break;
279
280                 if (jsobj)
281                 {
282                         if (json_object_get_type(jsobj) == json_type_object)
283                                 blobmsg_add_object(&buf, jsobj);
284
285                         json_object_put(jsobj);
286                         break;
287                 }
288         }
289
290         json_tokener_free(tok);
291
292         n_method = 0;
293
294         blob_for_each_attr(cur, buf.head, rem)
295                 n_method++;
296
297         if (!n_method)
298                 return NULL;
299
300         methods = calloc(n_method, sizeof(*methods));
301
302         if (!methods)
303                 return NULL;
304
305         n_method = 0;
306
307         blob_for_each_attr(cur, buf.head, rem)
308         {
309                 if (!rpc_plugin_parse_signature(cur, &methods[n_method]))
310                         continue;
311
312                 n_method++;
313         }
314
315         obj = calloc(1, sizeof(*obj));
316
317         if (!obj)
318                 return NULL;
319
320         obj_type = calloc(1, sizeof(*obj_type));
321
322         if (!obj_type)
323                 return NULL;
324
325         asprintf((char **)&obj_type->name, "luci-rpc-plugin-%s", name);
326         obj_type->methods = methods;
327         obj_type->n_methods = n_method;
328
329         obj->name = strdup(name);
330         obj->type = obj_type;
331         obj->methods = methods;
332         obj->n_methods = n_method;
333
334         return obj;
335 }
336
337 static int
338 rpc_plugin_register(struct ubus_context *ctx, const char *path)
339 {
340         pid_t pid;
341         int rv = UBUS_STATUS_NO_DATA, fd, fds[2];
342         const char *name;
343         struct ubus_object *plugin;
344
345         name = strrchr(path, '/');
346
347         if (!name)
348                 return UBUS_STATUS_INVALID_ARGUMENT;
349
350         if (pipe(fds))
351                 return UBUS_STATUS_UNKNOWN_ERROR;
352
353         switch ((pid = fork()))
354         {
355         case -1:
356                 return UBUS_STATUS_UNKNOWN_ERROR;
357
358         case 0:
359                 fd = open("/dev/null", O_RDWR);
360
361                 if (fd > -1)
362                 {
363                         dup2(fd, 0);
364                         dup2(fd, 2);
365
366                         if (fd > 2)
367                                 close(fd);
368                 }
369
370                 dup2(fds[1], 1);
371
372                 close(fds[0]);
373                 close(fds[1]);
374
375                 if (execl(path, path, "list", NULL))
376                         return UBUS_STATUS_UNKNOWN_ERROR;
377
378         default:
379                 plugin = rpc_plugin_parse_plugin(name + 1, fds[0]);
380
381                 if (!plugin)
382                         goto out;
383
384                 rv = ubus_add_object(ctx, plugin);
385
386 out:
387                 close(fds[0]);
388                 close(fds[1]);
389                 waitpid(pid, NULL, 0);
390
391                 return rv;
392         }
393 }
394
395 int rpc_plugin_api_init(struct ubus_context *ctx)
396 {
397         DIR *d;
398         int rv = 0;
399         struct stat s;
400         struct dirent *e;
401         char path[PATH_MAX];
402
403         d = opendir(RPC_PLUGIN_DIRECTORY);
404
405         if (!d)
406                 return UBUS_STATUS_NOT_FOUND;
407
408         while ((e = readdir(d)) != NULL)
409         {
410                 snprintf(path, sizeof(path) - 1, RPC_PLUGIN_DIRECTORY "/%s", e->d_name);
411
412                 if (stat(path, &s) || !S_ISREG(s.st_mode) || !(s.st_mode & S_IXUSR))
413                         continue;
414
415                 rv |= rpc_plugin_register(ctx, path);
416         }
417
418         closedir(d);
419
420         return rv;
421 }