Rename from "luci-rpcd" to "rpcd"
[project/rpcd.git] / plugin.c
1 /*
2  * rpcd - 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
21 static struct blob_buf buf;
22
23 struct rpc_plugin_lookup_context {
24         uint32_t id;
25         char *name;
26         bool found;
27 };
28
29 static void
30 rpc_plugin_lookup_plugin_cb(struct ubus_context *ctx,
31                             struct ubus_object_data *obj, void *priv)
32 {
33         struct rpc_plugin_lookup_context *c = priv;
34
35         if (c->id == obj->id)
36         {
37                 c->found = true;
38                 sprintf(c->name, "%s", obj->path);
39         }
40 }
41
42 static bool
43 rpc_plugin_lookup_plugin(struct ubus_context *ctx, struct ubus_object *obj,
44                          char *strptr)
45 {
46         struct rpc_plugin_lookup_context c = { .id = obj->id, .name = strptr };
47
48         if (ubus_lookup(ctx, NULL, rpc_plugin_lookup_plugin_cb, &c))
49                 return false;
50
51         return c.found;
52 }
53
54 struct call_context {
55         char path[PATH_MAX];
56         const char *argv[4];
57         char *method;
58         char *input;
59         json_tokener *tok;
60         json_object *obj;
61         bool input_done;
62         bool output_done;
63 };
64
65 static int
66 rpc_plugin_call_stdin_cb(struct ustream *s, void *priv)
67 {
68         struct call_context *c = priv;
69
70         if (!c->input_done)
71         {
72                 ustream_write(s, c->input, strlen(c->input), false);
73                 c->input_done = true;
74         }
75
76         return 0;
77 }
78
79 static int
80 rpc_plugin_call_stdout_cb(struct blob_buf *blob, char *buf, int len, void *priv)
81 {
82         struct call_context *c = priv;
83
84         if (!c->output_done)
85         {
86                 c->obj = json_tokener_parse_ex(c->tok, buf, len);
87
88                 if (json_tokener_get_error(c->tok) != json_tokener_continue)
89                         c->output_done = true;
90         }
91
92         return len;
93 }
94
95 static int
96 rpc_plugin_call_stderr_cb(struct blob_buf *blob, char *buf, int len, void *priv)
97 {
98         return len;
99 }
100
101 static int
102 rpc_plugin_call_finish_cb(struct blob_buf *blob, int stat, void *priv)
103 {
104         struct call_context *c = priv;
105         int rv = UBUS_STATUS_INVALID_ARGUMENT;
106
107         if (json_tokener_get_error(c->tok) == json_tokener_success)
108         {
109                 if (c->obj)
110                 {
111                         if (json_object_get_type(c->obj) == json_type_object ||
112                             json_object_get_type(c->obj) == json_type_array)
113                         {
114                                 blobmsg_add_json_element(blob, NULL, c->obj);
115                                 rv = UBUS_STATUS_OK;
116                         }
117
118                         json_object_put(c->obj);
119                 }
120                 else
121                 {
122                         rv = UBUS_STATUS_NO_DATA;
123                 }
124         }
125
126         json_tokener_free(c->tok);
127
128         free(c->input);
129         free(c->method);
130
131         return rv;
132 }
133
134 static int
135 rpc_plugin_call(struct ubus_context *ctx, struct ubus_object *obj,
136                 struct ubus_request_data *req, const char *method,
137                 struct blob_attr *msg)
138 {
139         int rv = UBUS_STATUS_UNKNOWN_ERROR;
140         struct call_context *c;
141         char *plugin;
142
143         c = calloc(1, sizeof(*c));
144
145         if (!c)
146                 goto fail;
147
148         c->method = strdup(method);
149         c->input = blobmsg_format_json(msg, true);
150         c->tok = json_tokener_new();
151
152         if (!c->method || !c->input || !c->tok)
153                 goto fail;
154
155         plugin = c->path + sprintf(c->path, "%s/", RPC_PLUGIN_DIRECTORY);
156
157         if (!rpc_plugin_lookup_plugin(ctx, obj, plugin))
158         {
159                 rv = UBUS_STATUS_NOT_FOUND;
160                 goto fail;
161         }
162
163         c->argv[0] = c->path;
164         c->argv[1] = "call";
165         c->argv[2] = c->method;
166
167         return rpc_exec(c->argv, rpc_plugin_call_stdin_cb,
168                         rpc_plugin_call_stdout_cb, rpc_plugin_call_stderr_cb,
169                         rpc_plugin_call_finish_cb, c, ctx, req);
170
171 fail:
172         if (c)
173         {
174                 if (c->method)
175                         free(c->method);
176
177                 if (c->input)
178                         free(c->input);
179
180                 if (c->tok)
181                         json_tokener_free(c->tok);
182
183                 free(c);
184         }
185
186         return rv;
187 }
188
189 static bool
190 rpc_plugin_parse_signature(struct blob_attr *sig, struct ubus_method *method)
191 {
192         int rem, n_attr;
193         enum blobmsg_type type;
194         struct blob_attr *attr;
195         struct blobmsg_policy *policy = NULL;
196
197         if (!sig || blob_id(sig) != BLOBMSG_TYPE_TABLE)
198                 return false;
199
200         n_attr = 0;
201
202         blobmsg_for_each_attr(attr, sig, rem)
203                 n_attr++;
204
205         if (n_attr)
206         {
207                 policy = calloc(n_attr, sizeof(*policy));
208
209                 if (!policy)
210                         return false;
211
212                 n_attr = 0;
213
214                 blobmsg_for_each_attr(attr, sig, rem)
215                 {
216                         type = blob_id(attr);
217
218                         if (type == BLOBMSG_TYPE_INT32)
219                         {
220                                 switch (blobmsg_get_u32(attr))
221                                 {
222                                 case 8:
223                                         type = BLOBMSG_TYPE_INT8;
224                                         break;
225
226                                 case 16:
227                                         type = BLOBMSG_TYPE_INT16;
228                                         break;
229
230                                 case 64:
231                                         type = BLOBMSG_TYPE_INT64;
232                                         break;
233
234                                 default:
235                                         type = BLOBMSG_TYPE_INT32;
236                                         break;
237                                 }
238                         }
239
240                         policy[n_attr].name = strdup(blobmsg_name(attr));
241                         policy[n_attr].type = type;
242
243                         n_attr++;
244                 }
245         }
246
247         method->name = strdup(blobmsg_name(sig));
248         method->handler = rpc_plugin_call;
249         method->policy = policy;
250         method->n_policy = n_attr;
251
252         return true;
253 }
254
255 static struct ubus_object *
256 rpc_plugin_parse_exec(const char *name, int fd)
257 {
258         int len, rem, n_method;
259         struct blob_attr *cur;
260         struct ubus_method *methods;
261         struct ubus_object_type *obj_type;
262         struct ubus_object *obj;
263         char outbuf[1024];
264
265         json_tokener *tok;
266         json_object *jsobj;
267
268         blob_buf_init(&buf, 0);
269
270         tok = json_tokener_new();
271
272         if (!tok)
273                 return NULL;
274
275         while ((len = read(fd, outbuf, sizeof(outbuf))) > 0)
276         {
277                 jsobj = json_tokener_parse_ex(tok, outbuf, len);
278
279                 if (json_tokener_get_error(tok) == json_tokener_continue)
280                         continue;
281
282                 if (json_tokener_get_error(tok) != json_tokener_success)
283                         break;
284
285                 if (jsobj)
286                 {
287                         if (json_object_get_type(jsobj) == json_type_object)
288                                 blobmsg_add_object(&buf, jsobj);
289
290                         json_object_put(jsobj);
291                         break;
292                 }
293         }
294
295         json_tokener_free(tok);
296
297         n_method = 0;
298
299         blob_for_each_attr(cur, buf.head, rem)
300                 n_method++;
301
302         if (!n_method)
303                 return NULL;
304
305         methods = calloc(n_method, sizeof(*methods));
306
307         if (!methods)
308                 return NULL;
309
310         n_method = 0;
311
312         blob_for_each_attr(cur, buf.head, rem)
313         {
314                 if (!rpc_plugin_parse_signature(cur, &methods[n_method]))
315                         continue;
316
317                 n_method++;
318         }
319
320         obj = calloc(1, sizeof(*obj));
321
322         if (!obj)
323                 return NULL;
324
325         obj_type = calloc(1, sizeof(*obj_type));
326
327         if (!obj_type)
328                 return NULL;
329
330         asprintf((char **)&obj_type->name, "luci-rpc-plugin-%s", name);
331         obj_type->methods = methods;
332         obj_type->n_methods = n_method;
333
334         obj->name = strdup(name);
335         obj->type = obj_type;
336         obj->methods = methods;
337         obj->n_methods = n_method;
338
339         return obj;
340 }
341
342 static int
343 rpc_plugin_register_exec(struct ubus_context *ctx, const char *path)
344 {
345         pid_t pid;
346         int rv = UBUS_STATUS_NO_DATA, fd, fds[2];
347         const char *name;
348         struct ubus_object *plugin;
349
350         name = strrchr(path, '/');
351
352         if (!name)
353                 return UBUS_STATUS_INVALID_ARGUMENT;
354
355         if (pipe(fds))
356                 return UBUS_STATUS_UNKNOWN_ERROR;
357
358         switch ((pid = fork()))
359         {
360         case -1:
361                 return UBUS_STATUS_UNKNOWN_ERROR;
362
363         case 0:
364                 fd = open("/dev/null", O_RDWR);
365
366                 if (fd > -1)
367                 {
368                         dup2(fd, 0);
369                         dup2(fd, 2);
370
371                         if (fd > 2)
372                                 close(fd);
373                 }
374
375                 dup2(fds[1], 1);
376
377                 close(fds[0]);
378                 close(fds[1]);
379
380                 if (execl(path, path, "list", NULL))
381                         return UBUS_STATUS_UNKNOWN_ERROR;
382
383         default:
384                 plugin = rpc_plugin_parse_exec(name + 1, fds[0]);
385
386                 if (!plugin)
387                         goto out;
388
389                 rv = ubus_add_object(ctx, plugin);
390
391 out:
392                 close(fds[0]);
393                 close(fds[1]);
394                 waitpid(pid, NULL, 0);
395
396                 return rv;
397         }
398 }
399
400
401 static LIST_HEAD(plugins);
402
403 static const struct rpc_daemon_ops ops = {
404         .access = rpc_session_access,
405         .exec   = rpc_exec,
406 };
407
408 static int
409 rpc_plugin_register_library(struct ubus_context *ctx, const char *path)
410 {
411         struct rpc_plugin *p;
412         void *dlh;
413
414         dlh = dlopen(path, RTLD_LAZY | RTLD_GLOBAL);
415
416         if (!dlh)
417                 return UBUS_STATUS_UNKNOWN_ERROR;
418
419         p = dlsym(dlh, "rpc_plugin");
420
421         if (!p)
422                 return UBUS_STATUS_NOT_FOUND;
423
424         list_add(&p->list, &plugins);
425
426         return p->init(&ops, ctx);
427 }
428
429 int rpc_plugin_api_init(struct ubus_context *ctx)
430 {
431         DIR *d;
432         int rv = 0;
433         struct stat s;
434         struct dirent *e;
435         char path[PATH_MAX];
436
437         if ((d = opendir(RPC_PLUGIN_DIRECTORY)) != NULL)
438         {
439                 while ((e = readdir(d)) != NULL)
440                 {
441                         snprintf(path, sizeof(path) - 1,
442                                  RPC_PLUGIN_DIRECTORY "/%s", e->d_name);
443
444                         if (stat(path, &s) || !S_ISREG(s.st_mode) || !(s.st_mode & S_IXUSR))
445                                 continue;
446
447                         rv |= rpc_plugin_register_exec(ctx, path);
448                 }
449
450                 closedir(d);
451         }
452
453         if ((d = opendir(RPC_LIBRARY_DIRECTORY)) != NULL)
454         {
455                 while ((e = readdir(d)) != NULL)
456                 {
457                         snprintf(path, sizeof(path) - 1,
458                                  RPC_LIBRARY_DIRECTORY "/%s", e->d_name);
459
460                         if (stat(path, &s) || !S_ISREG(s.st_mode))
461                                 continue;
462
463                         rv |= rpc_plugin_register_library(ctx, path);
464                 }
465
466                 closedir(d);
467         }
468
469         return rv;
470 }