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