• source navigation  • diff markup  • identifier search  • freetext search  • 

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

This page was automatically generated by LXR 0.3.1.  •  OpenWrt