sys: use "Auto-Installed" field for packagelist
[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 static void
55 rpc_plugin_json_array_to_blob(struct array_list *a, struct blob_buf *blob);
56
57 static void
58 rpc_plugin_json_object_to_blob(json_object *o, struct blob_buf *blob);
59
60 static void
61 rpc_plugin_json_element_to_blob(const char *name, json_object *val,
62 struct blob_buf *blob)
63 {
64 void *c;
65 int64_t n;
66
67 switch (json_object_get_type(val)) {
68 case json_type_object:
69 c = blobmsg_open_table(blob, name);
70 rpc_plugin_json_object_to_blob(val, blob);
71 blobmsg_close_table(blob, c);
72 break;
73
74 case json_type_array:
75 c = blobmsg_open_array(blob, name);
76 rpc_plugin_json_array_to_blob(json_object_get_array(val), blob);
77 blobmsg_close_array(blob, c);
78 break;
79
80 case json_type_string:
81 blobmsg_add_string(blob, name, json_object_get_string(val));
82 break;
83
84 case json_type_boolean:
85 blobmsg_add_u8(blob, name, json_object_get_boolean(val));
86 break;
87
88 case json_type_int:
89 n = json_object_get_int64(val);
90 if (n >= INT32_MIN && n <= INT32_MAX)
91 blobmsg_add_u32(blob, name, n);
92 else
93 blobmsg_add_u64(blob, name, n);
94 break;
95
96 case json_type_double:
97 blobmsg_add_double(blob, name, json_object_get_double(val));
98 break;
99
100 case json_type_null:
101 blobmsg_add_field(blob, BLOBMSG_TYPE_UNSPEC, name, NULL, 0);
102 break;
103 }
104 }
105
106 static void
107 rpc_plugin_json_array_to_blob(struct array_list *a, struct blob_buf *blob)
108 {
109 int i, len;
110
111 for (i = 0, len = array_list_length(a); i < len; i++)
112 rpc_plugin_json_element_to_blob(NULL, array_list_get_idx(a, i), blob);
113 }
114
115 static void
116 rpc_plugin_json_object_to_blob(json_object *o, struct blob_buf *blob)
117 {
118 json_object_object_foreach(o, key, val)
119 rpc_plugin_json_element_to_blob(key, val, blob);
120 }
121
122 struct call_context {
123 char path[PATH_MAX];
124 const char *argv[4];
125 char *method;
126 char *input;
127 json_tokener *tok;
128 json_object *obj;
129 bool input_done;
130 bool output_done;
131 };
132
133 static int
134 rpc_plugin_call_stdin_cb(struct ustream *s, void *priv)
135 {
136 struct call_context *c = priv;
137
138 if (!c->input_done)
139 {
140 ustream_write(s, c->input, strlen(c->input), false);
141 c->input_done = true;
142 }
143
144 return 0;
145 }
146
147 static int
148 rpc_plugin_call_stdout_cb(struct blob_buf *blob, char *buf, int len, void *priv)
149 {
150 struct call_context *c = priv;
151
152 if (!c->output_done)
153 {
154 c->obj = json_tokener_parse_ex(c->tok, buf, len);
155
156 if (json_tokener_get_error(c->tok) != json_tokener_continue)
157 c->output_done = true;
158 }
159
160 return len;
161 }
162
163 static int
164 rpc_plugin_call_stderr_cb(struct blob_buf *blob, char *buf, int len, void *priv)
165 {
166 return len;
167 }
168
169 static int
170 rpc_plugin_call_finish_cb(struct blob_buf *blob, int stat, void *priv)
171 {
172 struct call_context *c = priv;
173 int rv = UBUS_STATUS_INVALID_ARGUMENT;
174
175 if (json_tokener_get_error(c->tok) == json_tokener_success)
176 {
177 if (c->obj)
178 {
179 if (json_object_get_type(c->obj) == json_type_object)
180 {
181 rpc_plugin_json_object_to_blob(c->obj, blob);
182 rv = UBUS_STATUS_OK;
183 }
184
185 json_object_put(c->obj);
186 }
187 else
188 {
189 rv = UBUS_STATUS_NO_DATA;
190 }
191 }
192
193 json_tokener_free(c->tok);
194
195 free(c->input);
196
197 return rv;
198 }
199
200 static int
201 rpc_plugin_call(struct ubus_context *ctx, struct ubus_object *obj,
202 struct ubus_request_data *req, const char *method,
203 struct blob_attr *msg)
204 {
205 int rv = UBUS_STATUS_UNKNOWN_ERROR;
206 struct call_context *c;
207 char *plugin, *mptr;
208
209 c = calloc_a(sizeof(*c), &mptr, strlen(method) + 1);
210
211 if (!c)
212 goto fail;
213
214 c->method = strcpy(mptr, method);
215 c->input = blobmsg_format_json(msg, true);
216 c->tok = json_tokener_new();
217
218 if (!c->input || !c->tok)
219 goto fail;
220
221 plugin = c->path + sprintf(c->path, "%s/", RPC_PLUGIN_DIRECTORY);
222
223 if (!rpc_plugin_lookup_plugin(ctx, obj, plugin))
224 {
225 rv = UBUS_STATUS_NOT_FOUND;
226 goto fail;
227 }
228
229 c->argv[0] = c->path;
230 c->argv[1] = "call";
231 c->argv[2] = c->method;
232
233 rv = rpc_exec(c->argv, rpc_plugin_call_stdin_cb,
234 rpc_plugin_call_stdout_cb, rpc_plugin_call_stderr_cb,
235 rpc_plugin_call_finish_cb, c, ctx, req);
236
237 if (rv == UBUS_STATUS_OK)
238 return rv;
239
240 fail:
241 if (c)
242 {
243 if (c->input)
244 free(c->input);
245
246 if (c->tok)
247 json_tokener_free(c->tok);
248
249 free(c);
250 }
251
252 return rv;
253 }
254
255 static bool
256 rpc_plugin_parse_signature(struct blob_attr *sig, struct ubus_method *method)
257 {
258 int rem, n_attr;
259 enum blobmsg_type type;
260 struct blob_attr *attr;
261 struct blobmsg_policy *policy = NULL;
262
263 if (!sig || blobmsg_type(sig) != BLOBMSG_TYPE_TABLE)
264 return false;
265
266 n_attr = 0;
267
268 blobmsg_for_each_attr(attr, sig, rem)
269 n_attr++;
270
271 if (n_attr)
272 {
273 policy = calloc(n_attr, sizeof(*policy));
274
275 if (!policy)
276 return false;
277
278 n_attr = 0;
279
280 blobmsg_for_each_attr(attr, sig, rem)
281 {
282 type = blobmsg_type(attr);
283
284 if (type == BLOBMSG_TYPE_INT32)
285 {
286 switch (blobmsg_get_u32(attr))
287 {
288 case 8:
289 type = BLOBMSG_TYPE_INT8;
290 break;
291
292 case 16:
293 type = BLOBMSG_TYPE_INT16;
294 break;
295
296 case 64:
297 type = BLOBMSG_TYPE_INT64;
298 break;
299
300 default:
301 type = BLOBMSG_TYPE_INT32;
302 break;
303 }
304 }
305
306 policy[n_attr].name = strdup(blobmsg_name(attr));
307 policy[n_attr].type = type;
308
309 n_attr++;
310 }
311 }
312
313 method->name = strdup(blobmsg_name(sig));
314 method->handler = rpc_plugin_call;
315 method->policy = policy;
316 method->n_policy = n_attr;
317
318 return true;
319 }
320
321 static struct ubus_object *
322 rpc_plugin_parse_exec(const char *name, int fd)
323 {
324 int len, rem, n_method;
325 struct blob_attr *cur;
326 struct ubus_method *methods;
327 struct ubus_object_type *obj_type;
328 struct ubus_object *obj;
329 char outbuf[1024];
330
331 json_tokener *tok;
332 json_object *jsobj;
333
334 blob_buf_init(&buf, 0);
335
336 tok = json_tokener_new();
337
338 if (!tok)
339 return NULL;
340
341 while ((len = read(fd, outbuf, sizeof(outbuf))) > 0)
342 {
343 jsobj = json_tokener_parse_ex(tok, outbuf, len);
344
345 if (json_tokener_get_error(tok) == json_tokener_continue)
346 continue;
347
348 if (json_tokener_get_error(tok) != json_tokener_success)
349 break;
350
351 if (jsobj)
352 {
353 if (json_object_get_type(jsobj) == json_type_object)
354 blobmsg_add_object(&buf, jsobj);
355
356 json_object_put(jsobj);
357 break;
358 }
359 }
360
361 json_tokener_free(tok);
362
363 n_method = 0;
364
365 blob_for_each_attr(cur, buf.head, rem)
366 n_method++;
367
368 if (!n_method)
369 return NULL;
370
371 methods = calloc(n_method, sizeof(*methods));
372
373 if (!methods)
374 return NULL;
375
376 n_method = 0;
377
378 blob_for_each_attr(cur, buf.head, rem)
379 {
380 if (!rpc_plugin_parse_signature(cur, &methods[n_method]))
381 continue;
382
383 n_method++;
384 }
385
386 obj = calloc(1, sizeof(*obj));
387
388 if (!obj)
389 return NULL;
390
391 obj_type = calloc(1, sizeof(*obj_type));
392
393 if (!obj_type) {
394 free(obj);
395 return NULL;
396 }
397
398 if (asprintf((char **)&obj_type->name, "rpcd-plugin-exec-%s", name) < 0) {
399 free(obj);
400 free(obj_type);
401 return NULL;
402 }
403
404 obj_type->methods = methods;
405 obj_type->n_methods = n_method;
406
407 obj->name = strdup(name);
408 obj->type = obj_type;
409 obj->methods = methods;
410 obj->n_methods = n_method;
411
412 return obj;
413 }
414
415 static int
416 rpc_plugin_register_exec(struct ubus_context *ctx, const char *path)
417 {
418 pid_t pid;
419 int rv = UBUS_STATUS_NO_DATA, fd, fds[2];
420 const char *name;
421 struct ubus_object *plugin;
422
423 name = strrchr(path, '/');
424
425 if (!name)
426 return UBUS_STATUS_INVALID_ARGUMENT;
427
428 if (pipe(fds))
429 return UBUS_STATUS_UNKNOWN_ERROR;
430
431 switch ((pid = fork()))
432 {
433 case -1:
434 return UBUS_STATUS_UNKNOWN_ERROR;
435
436 case 0:
437 fd = open("/dev/null", O_RDWR);
438
439 if (fd > -1)
440 {
441 dup2(fd, 0);
442 dup2(fd, 2);
443
444 if (fd > 2)
445 close(fd);
446 }
447
448 dup2(fds[1], 1);
449
450 close(fds[0]);
451 close(fds[1]);
452
453 if (execl(path, path, "list", NULL))
454 return UBUS_STATUS_UNKNOWN_ERROR;
455
456 default:
457 plugin = rpc_plugin_parse_exec(name + 1, fds[0]);
458
459 if (!plugin)
460 goto out;
461
462 rv = ubus_add_object(ctx, plugin);
463
464 out:
465 close(fds[0]);
466 close(fds[1]);
467 waitpid(pid, NULL, 0);
468
469 return rv;
470 }
471 }
472
473
474 static LIST_HEAD(plugins);
475
476 static const struct rpc_daemon_ops ops = {
477 .session_access = rpc_session_access,
478 .session_create_cb = rpc_session_create_cb,
479 .session_destroy_cb = rpc_session_destroy_cb,
480 .exec = rpc_exec,
481 .exec_timeout = &rpc_exec_timeout,
482 };
483
484 static int
485 rpc_plugin_register_library(struct ubus_context *ctx, const char *path)
486 {
487 struct rpc_plugin *p;
488 void *dlh;
489
490 dlh = dlopen(path, RTLD_LAZY | RTLD_LOCAL);
491
492 if (!dlh) {
493 fprintf(stderr, "Failed to load plugin %s: %s\n",
494 path, dlerror());
495
496 return UBUS_STATUS_UNKNOWN_ERROR;
497 }
498
499 p = dlsym(dlh, "rpc_plugin");
500
501 if (!p)
502 return UBUS_STATUS_NOT_FOUND;
503
504 list_add(&p->list, &plugins);
505
506 return p->init(&ops, ctx);
507 }
508
509 int rpc_plugin_api_init(struct ubus_context *ctx)
510 {
511 DIR *d;
512 int rv = 0;
513 struct stat s;
514 struct dirent *e;
515 char path[PATH_MAX];
516
517 if ((d = opendir(RPC_PLUGIN_DIRECTORY)) != NULL)
518 {
519 while ((e = readdir(d)) != NULL)
520 {
521 snprintf(path, sizeof(path) - 1,
522 RPC_PLUGIN_DIRECTORY "/%s", e->d_name);
523
524 if (stat(path, &s) || !S_ISREG(s.st_mode) || !(s.st_mode & S_IXUSR))
525 continue;
526
527 rv |= rpc_plugin_register_exec(ctx, path);
528 }
529
530 closedir(d);
531 }
532
533 if ((d = opendir(RPC_LIBRARY_DIRECTORY)) != NULL)
534 {
535 while ((e = readdir(d)) != NULL)
536 {
537 snprintf(path, sizeof(path) - 1,
538 RPC_LIBRARY_DIRECTORY "/%s", e->d_name);
539
540 if (stat(path, &s) || !S_ISREG(s.st_mode))
541 continue;
542
543 rv |= rpc_plugin_register_library(ctx, path);
544 }
545
546 closedir(d);
547 }
548
549 return rv;
550 }