d6bdcef516a69bf788cbb30d36be6f0553c3639e
[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_stderr_cb(struct blob_buf *blob, char *buf, int len, void *priv)
98 {
99         return len;
100 }
101
102 static int
103 rpc_plugin_call_finish_cb(struct blob_buf *blob, int stat, void *priv)
104 {
105         struct call_context *c = priv;
106         int rv = UBUS_STATUS_INVALID_ARGUMENT;
107
108         if (json_tokener_get_error(c->tok) == json_tokener_success)
109         {
110                 if (c->obj)
111                 {
112                         if (json_object_get_type(c->obj) == json_type_object ||
113                             json_object_get_type(c->obj) == json_type_array)
114                         {
115                                 blobmsg_add_json_element(blob, NULL, c->obj);
116                                 rv = UBUS_STATUS_OK;
117                         }
118
119                         json_object_put(c->obj);
120                 }
121                 else
122                 {
123                         rv = UBUS_STATUS_NO_DATA;
124                 }
125         }
126
127         json_tokener_free(c->tok);
128
129         free(c->input);
130         free(c->method);
131
132         return rv;
133 }
134
135 static int
136 rpc_plugin_call(struct ubus_context *ctx, struct ubus_object *obj,
137                 struct ubus_request_data *req, const char *method,
138                 struct blob_attr *msg)
139 {
140         int rv = UBUS_STATUS_UNKNOWN_ERROR;
141         struct call_context *c;
142         char *plugin;
143
144         c = calloc(1, sizeof(*c));
145
146         if (!c)
147                 goto fail;
148
149         c->method = strdup(method);
150         c->input = blobmsg_format_json(msg, true);
151         c->tok = json_tokener_new();
152
153         if (!c->method || !c->input || !c->tok)
154                 goto fail;
155
156         plugin = c->path + sprintf(c->path, "%s/", RPC_PLUGIN_DIRECTORY);
157
158         if (!rpc_plugin_lookup_plugin(ctx, obj, plugin))
159         {
160                 rv = UBUS_STATUS_NOT_FOUND;
161                 goto fail;
162         }
163
164         c->argv[0] = c->path;
165         c->argv[1] = "call";
166         c->argv[2] = c->method;
167
168         return rpc_exec(c->argv, rpc_plugin_call_stdin_cb,
169                         rpc_plugin_call_stdout_cb, rpc_plugin_call_stderr_cb,
170                         rpc_plugin_call_finish_cb, c, ctx, req);
171
172 fail:
173         if (c)
174         {
175                 if (c->method)
176                         free(c->method);
177
178                 if (c->input)
179                         free(c->input);
180
181                 if (c->tok)
182                         json_tokener_free(c->tok);
183
184                 free(c);
185         }
186
187         return rv;
188 }
189
190 static bool
191 rpc_plugin_parse_signature(struct blob_attr *sig, struct ubus_method *method)
192 {
193         int rem, n_attr;
194         enum blobmsg_type type;
195         struct blob_attr *attr;
196         struct blobmsg_policy *policy = NULL;
197
198         if (!sig || blob_id(sig) != BLOBMSG_TYPE_TABLE)
199                 return false;
200
201         n_attr = 0;
202
203         blobmsg_for_each_attr(attr, sig, rem)
204                 n_attr++;
205
206         if (n_attr)
207         {
208                 policy = calloc(n_attr, sizeof(*policy));
209
210                 if (!policy)
211                         return false;
212
213                 n_attr = 0;
214
215                 blobmsg_for_each_attr(attr, sig, rem)
216                 {
217                         type = blob_id(attr);
218
219                         if (type == BLOBMSG_TYPE_INT32)
220                         {
221                                 switch (blobmsg_get_u32(attr))
222                                 {
223                                 case 8:
224                                         type = BLOBMSG_TYPE_INT8;
225                                         break;
226
227                                 case 16:
228                                         type = BLOBMSG_TYPE_INT16;
229                                         break;
230
231                                 case 64:
232                                         type = BLOBMSG_TYPE_INT64;
233                                         break;
234
235                                 default:
236                                         type = BLOBMSG_TYPE_INT32;
237                                         break;
238                                 }
239                         }
240
241                         policy[n_attr].name = strdup(blobmsg_name(attr));
242                         policy[n_attr].type = type;
243
244                         n_attr++;
245                 }
246         }
247
248         method->name = strdup(blobmsg_name(sig));
249         method->handler = rpc_plugin_call;
250         method->policy = policy;
251         method->n_policy = n_attr;
252
253         return true;
254 }
255
256 static struct ubus_object *
257 rpc_plugin_parse_plugin(const char *name, int fd)
258 {
259         int len, rem, n_method;
260         struct blob_attr *cur;
261         struct ubus_method *methods;
262         struct ubus_object_type *obj_type;
263         struct ubus_object *obj;
264         char outbuf[1024];
265
266         json_tokener *tok;
267         json_object *jsobj;
268
269         blob_buf_init(&buf, 0);
270
271         tok = json_tokener_new();
272
273         if (!tok)
274                 return NULL;
275
276         while ((len = read(fd, outbuf, sizeof(outbuf))) > 0)
277         {
278                 jsobj = json_tokener_parse_ex(tok, outbuf, len);
279
280                 if (json_tokener_get_error(tok) == json_tokener_continue)
281                         continue;
282
283                 if (json_tokener_get_error(tok) != json_tokener_success)
284                         break;
285
286                 if (jsobj)
287                 {
288                         if (json_object_get_type(jsobj) == json_type_object)
289                                 blobmsg_add_object(&buf, jsobj);
290
291                         json_object_put(jsobj);
292                         break;
293                 }
294         }
295
296         json_tokener_free(tok);
297
298         n_method = 0;
299
300         blob_for_each_attr(cur, buf.head, rem)
301                 n_method++;
302
303         if (!n_method)
304                 return NULL;
305
306         methods = calloc(n_method, sizeof(*methods));
307
308         if (!methods)
309                 return NULL;
310
311         n_method = 0;
312
313         blob_for_each_attr(cur, buf.head, rem)
314         {
315                 if (!rpc_plugin_parse_signature(cur, &methods[n_method]))
316                         continue;
317
318                 n_method++;
319         }
320
321         obj = calloc(1, sizeof(*obj));
322
323         if (!obj)
324                 return NULL;
325
326         obj_type = calloc(1, sizeof(*obj_type));
327
328         if (!obj_type)
329                 return NULL;
330
331         asprintf((char **)&obj_type->name, "luci-rpc-plugin-%s", name);
332         obj_type->methods = methods;
333         obj_type->n_methods = n_method;
334
335         obj->name = strdup(name);
336         obj->type = obj_type;
337         obj->methods = methods;
338         obj->n_methods = n_method;
339
340         return obj;
341 }
342
343 static int
344 rpc_plugin_register(struct ubus_context *ctx, const char *path)
345 {
346         pid_t pid;
347         int rv = UBUS_STATUS_NO_DATA, fd, fds[2];
348         const char *name;
349         struct ubus_object *plugin;
350
351         name = strrchr(path, '/');
352
353         if (!name)
354                 return UBUS_STATUS_INVALID_ARGUMENT;
355
356         if (pipe(fds))
357                 return UBUS_STATUS_UNKNOWN_ERROR;
358
359         switch ((pid = fork()))
360         {
361         case -1:
362                 return UBUS_STATUS_UNKNOWN_ERROR;
363
364         case 0:
365                 fd = open("/dev/null", O_RDWR);
366
367                 if (fd > -1)
368                 {
369                         dup2(fd, 0);
370                         dup2(fd, 2);
371
372                         if (fd > 2)
373                                 close(fd);
374                 }
375
376                 dup2(fds[1], 1);
377
378                 close(fds[0]);
379                 close(fds[1]);
380
381                 if (execl(path, path, "list", NULL))
382                         return UBUS_STATUS_UNKNOWN_ERROR;
383
384         default:
385                 plugin = rpc_plugin_parse_plugin(name + 1, fds[0]);
386
387                 if (!plugin)
388                         goto out;
389
390                 rv = ubus_add_object(ctx, plugin);
391
392 out:
393                 close(fds[0]);
394                 close(fds[1]);
395                 waitpid(pid, NULL, 0);
396
397                 return rv;
398         }
399 }
400
401 int rpc_plugin_api_init(struct ubus_context *ctx)
402 {
403         DIR *d;
404         int rv = 0;
405         struct stat s;
406         struct dirent *e;
407         char path[PATH_MAX];
408
409         d = opendir(RPC_PLUGIN_DIRECTORY);
410
411         if (!d)
412                 return UBUS_STATUS_NOT_FOUND;
413
414         while ((e = readdir(d)) != NULL)
415         {
416                 snprintf(path, sizeof(path) - 1, RPC_PLUGIN_DIRECTORY "/%s", e->d_name);
417
418                 if (stat(path, &s) || !S_ISREG(s.st_mode) || !(s.st_mode & S_IXUSR))
419                         continue;
420
421                 rv |= rpc_plugin_register(ctx, path);
422         }
423
424         closedir(d);
425
426         return rv;
427 }