gam-resource-manager: adjust to updated proxied call callback signature.
[profile/ivi/murphy.git] / src / core / plugin.c
1 /*
2  * Copyright (c) 2012, Intel Corporation
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are
6  * met:
7  *
8  *  * Redistributions of source code must retain the above copyright notice,
9  *    this list of conditions and the following disclaimer.
10  *  * Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *  * Neither the name of Intel Corporation nor the names of its contributors
14  *    may be used to endorse or promote products derived from this software
15  *    without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  */
29
30 #include <unistd.h>
31 #include <errno.h>
32 #include <stdarg.h>
33 #include <limits.h>
34 #include <sys/stat.h>
35 #include <sys/types.h>
36
37 #include <murphy/common/list.h>
38 #include <murphy/common/file-utils.h>
39 #include <murphy/core/plugin.h>
40
41 #define PLUGIN_PREFIX "plugin-"
42 #define BUILTIN TRUE
43 #define DYNAMIC FALSE
44
45 #define __PARANOID_BLACKLIST_CHECK__
46
47 static mrp_plugin_descr_t *open_builtin(mrp_context_t *ctx, const char *name);
48 static mrp_plugin_descr_t *open_dynamic(mrp_context_t *ctx, const char *name,
49                                         void **handle);
50 static mrp_plugin_t *find_plugin(mrp_context_t *ctx, char *name);
51 static mrp_plugin_t *find_plugin_instance(mrp_context_t *ctx,
52                                           const char *instance);
53 static int parse_plugin_args(mrp_plugin_t *plugin,
54                              mrp_plugin_arg_t *argv, int argc);
55
56 static int export_plugin_methods(mrp_plugin_t *plugin);
57 static int remove_plugin_methods(mrp_plugin_t *plugin);
58 static int import_plugin_methods(mrp_plugin_t *plugin);
59 static int release_plugin_methods(mrp_plugin_t *plugin);
60
61
62 /*
63  * list of built-in in plugins
64  *
65  * Descriptors of built-in plugins, ie. plugins that are linked to
66  * the daemon, get collected to this list during startup.
67  */
68
69 static MRP_LIST_HOOK(builtin_plugins);
70
71
72 /*
73  * plugin-related events
74  */
75
76 enum {
77     PLUGIN_EVENT_LOADED = 0,             /* plugin has been loaded */
78     PLUGIN_EVENT_STARTED,                /* plugin has been started */
79     PLUGIN_EVENT_FAILED,                 /* plugin failed to start */
80     PLUGIN_EVENT_STOPPING,               /* plugin being stopped */
81     PLUGIN_EVENT_STOPPED,                /* plugin has been stopped */
82     PLUGIN_EVENT_UNLOADED,               /* plugin has been unloaded */
83     PLUGIN_EVENT_MAX,
84 };
85
86
87 MRP_REGISTER_EVENTS(plugin_events,
88                     MRP_EVENT(MRP_PLUGIN_EVENT_LOADED  , PLUGIN_EVENT_LOADED  ),
89                     MRP_EVENT(MRP_PLUGIN_EVENT_STARTED , PLUGIN_EVENT_STARTED ),
90                     MRP_EVENT(MRP_PLUGIN_EVENT_FAILED  , PLUGIN_EVENT_FAILED  ),
91                     MRP_EVENT(MRP_PLUGIN_EVENT_STOPPING, PLUGIN_EVENT_STOPPING),
92                     MRP_EVENT(MRP_PLUGIN_EVENT_STOPPED , PLUGIN_EVENT_STOPPED ),
93                     MRP_EVENT(MRP_PLUGIN_EVENT_UNLOADED, PLUGIN_EVENT_UNLOADED));
94
95
96 static inline int is_listed(const char *list,  const char *name)
97 {
98     const char *b, *n, *e;
99
100     if (list == NULL || name == NULL)
101         return FALSE;
102
103     if ((list[0] == '*' && list[1] == '\0') || (!strcmp(list, "all")))
104         return TRUE;
105
106     b = list;
107     while (b && *b) {
108         while (*b == ' ' || *b == '\t' || *b == ',')
109             b++;
110
111         if ((e = n = strchr(b, ',')) != NULL) {
112             while (e > b && (*e == ' ' || *e == '\t' || *e == ','))
113                 e--;
114         }
115
116         if ((e != NULL && e > b && !strncmp(b, name, e - b)) ||
117             (e == NULL &&          !strcmp (b, name)))
118             return TRUE;
119
120         if ((b[0] == '*' && (b[1] == ',' ||
121                              b[1] == ' ' || b[1] == '\t' ||
122                              b[1] == '\0')) ||
123             (b[0] == 'a' && b[1] == 'l' && b[2] == 'l' &&
124              (b[1] == ',' ||
125               b[1] == ' ' || b[1] == '\t' ||
126               b[1] == '\0'))) {
127             mrp_log_warning("Wildcard entry in blacklist/whitelist '%s'", list);
128             return TRUE;
129         }
130
131         b = n ? n + 1 : NULL;
132     }
133
134     return FALSE;
135 }
136
137
138 static inline int is_blacklisted(mrp_context_t *ctx, const char *name,
139                                  int builtin)
140 {
141 #define IS_WILDCARD(l) \
142     (((l) != NULL && (l)[0] == '*' && (l)[1] == '\0') ||        \
143      ((l) != NULL && !strcmp((l), "all")))
144
145     const char *bl, *wl, *type_bl, *type_wl;
146     int         b, w, tb, tw, blacklist;
147
148     mrp_debug("checking if %s plugin %s is blacklisted",
149               builtin ? "builtin" : "dynamic", name);
150
151     bl = ctx->blacklist_plugins;
152     wl = ctx->whitelist_plugins;
153     b  = is_listed(bl, name);
154     w  = is_listed(wl, name);
155
156     if (builtin) {
157         type_bl = ctx->blacklist_builtin;
158         type_wl = ctx->whitelist_builtin;
159     }
160     else {
161         type_bl = ctx->blacklist_dynamic;
162         type_wl = ctx->whitelist_dynamic;
163     }
164
165     tb = is_listed(type_bl, name);
166     tw = is_listed(type_wl, name);
167
168     mrp_debug("%s: b:(%s,%s), w:(%s,%s)", name,
169               b ? "true" : "false", tb ? "true" : "false",
170               w ? "true" : "false", tw ? "true" : "false");
171
172     if (IS_WILDCARD(bl) || IS_WILDCARD(type_bl)) {
173         if (w || tw)
174             blacklist = FALSE;
175          else
176             blacklist = TRUE;
177
178         goto verdict;
179     }
180
181     if (IS_WILDCARD(wl) || IS_WILDCARD(type_wl)) {
182         if (b || tb)
183             blacklist = TRUE;
184         else
185             blacklist = FALSE;
186
187         goto verdict;
188     }
189
190     blacklist = (( is_listed(bl, name) || is_listed(type_bl, name)) &&
191                  !(is_listed(wl, name) || is_listed(type_wl, name)));
192
193
194  verdict:
195     mrp_debug("%s: %sblacklisted", name, blacklist ? "" : "not ");
196
197     return blacklist;
198 }
199
200
201 static int emit_plugin_event(int idx, mrp_plugin_t *plugin)
202 {
203     mrp_context_t  *ctx   = plugin->ctx;
204     mrp_mainloop_t *ml    = ctx->ml;
205     uint16_t        name  = MRP_PLUGIN_TAG_PLUGIN;
206     uint16_t        inst  = MRP_PLUGIN_TAG_INSTANCE;
207     int             flags = MRP_EVENT_SYNCHRONOUS;
208
209     if (ctx->plugin_bus == NULL)
210         ctx->plugin_bus = mrp_event_bus_get(ml, MRP_PLUGIN_BUS);
211
212     if (mrp_event_emit_msg(ctx->plugin_bus, plugin_events[idx].id, flags,
213                            MRP_MSG_TAG_STRING(name, plugin->descriptor->name),
214                            MRP_MSG_TAG_STRING(inst, plugin->instance)) < 0)
215         return FALSE;
216     else
217         return TRUE;
218 }
219
220
221 int mrp_register_builtin_plugin(mrp_plugin_descr_t *descriptor)
222 {
223     mrp_plugin_t *plugin;
224
225     if (descriptor->name && descriptor->init && descriptor->exit) {
226         if ((plugin = mrp_allocz(sizeof(*plugin))) != NULL) {
227             plugin->descriptor = descriptor;
228             mrp_list_append(&builtin_plugins, &plugin->hook);
229
230             return TRUE;
231         }
232         else
233             return FALSE;
234     }
235     else {
236         mrp_log_error("Ignoring static plugin '%s' with an invalid or "
237                       "incomplete plugin descriptor.", descriptor->path);
238         return FALSE;
239     }
240 }
241
242
243 void mrp_block_blacklisted_plugins(mrp_context_t *ctx)
244 {
245     mrp_list_hook_t *p, *n;
246     mrp_plugin_t    *builtin;
247
248     mrp_list_foreach(&builtin_plugins, p, n) {
249         builtin = mrp_list_entry(p, typeof(*builtin), hook);
250
251         if (is_blacklisted(ctx, builtin->descriptor->name, BUILTIN)) {
252             mrp_log_info("Unlinking blacklisted builtin plugin %s",
253                          builtin->descriptor->name);
254             mrp_list_delete(&builtin->hook);
255         }
256     }
257 }
258
259
260 int mrp_plugin_exists(mrp_context_t *ctx, const char *name)
261 {
262     struct stat st;
263     char        path[PATH_MAX];
264
265     if (open_builtin(ctx, name))
266         return TRUE;
267     else {
268         if (is_blacklisted(ctx, name, DYNAMIC))
269             return FALSE;
270
271         snprintf(path, sizeof(path), "%s/%s%s.so", ctx->plugin_dir,
272                  PLUGIN_PREFIX, name);
273         if (stat(path, &st) == 0)
274             return TRUE;
275         else
276             return FALSE;
277     }
278 }
279
280
281 int mrp_plugin_loaded(mrp_context_t *ctx, const char *name)
282 {
283     mrp_list_hook_t *p, *n;
284     mrp_plugin_t    *plugin;
285
286     mrp_list_foreach(&ctx->plugins, p, n) {
287         plugin = mrp_list_entry(p, typeof(*plugin), hook);
288
289         if (!strcmp(plugin->instance, name))
290             return TRUE;
291     }
292
293     return FALSE;
294 }
295
296
297 int mrp_plugin_running(mrp_context_t *ctx, const char *name)
298 {
299     mrp_list_hook_t *p, *n;
300     mrp_plugin_t    *plugin;
301
302     mrp_list_foreach(&ctx->plugins, p, n) {
303         plugin = mrp_list_entry(p, typeof(*plugin), hook);
304
305         if (!strcmp(plugin->instance, name))
306             return (plugin->state == MRP_PLUGIN_RUNNING);
307     }
308
309     return FALSE;
310 }
311
312
313 static inline int check_plugin_version(mrp_plugin_descr_t *descr)
314 {
315     int major, minor;
316
317     major = MRP_VERSION_MAJOR(descr->mrp_version);
318     minor = MRP_VERSION_MINOR(descr->mrp_version);
319
320     if (major != MRP_PLUGIN_API_MAJOR || minor > MRP_PLUGIN_API_MINOR) {
321         mrp_log_error("Plugin '%s' uses incompatible version (%d.%d vs. %d.%d)",
322                       descr->name, major, minor,
323                       MRP_PLUGIN_API_MAJOR, MRP_PLUGIN_API_MINOR);
324         return FALSE;
325     }
326
327     return TRUE;
328 }
329
330
331 static inline int check_plugin_singleton(mrp_plugin_descr_t *descr)
332 {
333     if (descr->singleton && descr->ninstance > 1) {
334         mrp_log_error("Singleton plugin '%s' has already been instantiated.",
335                       descr->name);
336         return FALSE;
337     }
338     else
339         return TRUE;
340 }
341
342
343 mrp_plugin_t *mrp_load_plugin(mrp_context_t *ctx, const char *name,
344                               const char *instance, mrp_plugin_arg_t *args,
345                               int narg)
346 {
347     mrp_plugin_t        *plugin;
348     char                 path[PATH_MAX];
349     mrp_plugin_descr_t  *dynamic, *builtin, *descr;
350     void                *handle;
351     mrp_console_group_t *cmds;
352     char                 grpbuf[PATH_MAX], *cmdgrp;
353
354     if (name == NULL)
355         return NULL;
356
357     if (instance == NULL)
358         instance = name;
359
360     if (ctx->state == MRP_STATE_RUNNING && ctx->disable_runtime_load) {
361         mrp_log_error("Post-startup plugin loading has been disabled.");
362         return NULL;
363     }
364
365     if (find_plugin_instance(ctx, instance) != NULL) {
366         mrp_log_error("Plugin '%s' has already been loaded.", instance);
367         return NULL;
368     }
369
370     plugin = NULL;
371     handle = NULL;
372     snprintf(path, sizeof(path), "%s/%s%s.so", ctx->plugin_dir,
373              PLUGIN_PREFIX, name);
374
375     dynamic = open_dynamic(ctx, name, &handle);
376     builtin = open_builtin(ctx, name);
377
378     if (dynamic != NULL) {
379         if (builtin != NULL)
380             mrp_log_warning("Dynamic plugin '%s' shadows builtin plugin '%s'.",
381                             path, builtin->path);
382         descr = dynamic;
383     }
384     else {
385         if (builtin == NULL) {
386             mrp_log_error("Could not find plugin '%s' to load '%s'.", name,
387                           instance);
388             return NULL;
389         }
390         descr = builtin;
391     }
392
393     descr->ninstance++;
394
395     if (!check_plugin_version(descr) || !check_plugin_singleton(descr))
396         goto fail;
397
398     if ((plugin = mrp_allocz(sizeof(*plugin))) != NULL) {
399         mrp_list_init(&plugin->hook);
400
401         plugin->instance = mrp_strdup(instance);
402         plugin->path     = mrp_strdup(handle ? path : descr->path);
403
404         if (plugin->instance == NULL || plugin->path == NULL) {
405             mrp_log_error("Failed to allocate plugin '%s'.", name);
406             goto fail;
407         }
408
409         if (descr->cmds != NULL) {
410             plugin->cmds = cmds = mrp_allocz(sizeof(*plugin->cmds));
411
412             if (cmds != NULL) {
413                 mrp_list_init(&cmds->hook);
414
415                 if (instance != name) {
416                     snprintf(grpbuf, sizeof(grpbuf), "%s-%s", name, instance);
417                     cmdgrp = grpbuf;
418                 }
419                 else
420                     cmdgrp = (char *)name;
421
422                 cmds->name = mrp_strdup(cmdgrp);
423
424                 if (cmds->name == NULL) {
425                     mrp_log_error("Failed to allocate plugin commands.");
426                     goto fail;
427                 }
428
429                 cmds->commands = descr->cmds->commands;
430                 cmds->ncommand = descr->cmds->ncommand;
431
432                 if (MRP_UNLIKELY(descr->cmds->user_data != NULL))
433                     cmds->user_data = descr->cmds->user_data;
434                 else
435                     cmds->user_data = plugin;
436             }
437             else {
438                 mrp_log_error("Failed to allocate plugin commands.");
439                 goto fail;
440             }
441         }
442
443
444         plugin->ctx        = ctx;
445         plugin->descriptor = descr;
446         plugin->handle     = handle;
447
448         mrp_refcnt_init(&plugin->refcnt);
449
450         if (!parse_plugin_args(plugin, args, narg))
451             goto fail;
452
453         if (plugin->cmds != NULL)
454             mrp_console_add_group(plugin->ctx, plugin->cmds);
455
456         if (!export_plugin_methods(plugin))
457             goto fail;
458
459         mrp_list_append(&ctx->plugins, &plugin->hook);
460
461         emit_plugin_event(PLUGIN_EVENT_LOADED, plugin);
462
463         if (ctx->state == MRP_STATE_STARTING || ctx->state == MRP_STATE_RUNNING)
464             mrp_start_plugin(plugin);
465
466         return plugin;
467     }
468     else {
469         mrp_log_error("Could not allocate plugin '%s'.", name);
470     fail:
471         mrp_unload_plugin(plugin);
472
473         return NULL;
474     }
475 }
476
477
478 static int load_plugin_cb(const char *file, mrp_dirent_type_t type, void *data)
479 {
480     mrp_context_t *ctx = (mrp_context_t *)data;
481     char           name[PATH_MAX], *start, *end;
482     int            len;
483
484     MRP_UNUSED(type);
485
486     if ((start = strstr(file, PLUGIN_PREFIX)) != NULL) {
487         start += sizeof(PLUGIN_PREFIX) - 1;
488         if ((end = strstr(start, ".so")) != NULL) {
489             len = end - start;
490             strncpy(name, start, len);
491             name[len] = '\0';
492             mrp_load_plugin(ctx, name, NULL, NULL, 0);
493         }
494     }
495
496     return TRUE;
497 }
498
499
500 int mrp_load_all_plugins(mrp_context_t *ctx)
501 {
502     mrp_dirent_type_t  type;
503     const char        *pattern;
504     mrp_list_hook_t   *p, *n;
505     mrp_plugin_t      *plugin;
506
507     type    = MRP_DIRENT_REG;
508     pattern = PLUGIN_PREFIX".*\\.so$";
509     mrp_scan_dir(ctx->plugin_dir, pattern, type, load_plugin_cb, ctx);
510
511     mrp_list_foreach(&builtin_plugins, p, n) {
512         plugin = mrp_list_entry(p, typeof(*plugin), hook);
513
514         mrp_load_plugin(ctx, plugin->descriptor->name, NULL, NULL, 0);
515     }
516
517     return TRUE;
518 }
519
520
521 int mrp_request_plugin(mrp_context_t *ctx, const char *name,
522                        const char *instance)
523 {
524     mrp_plugin_t *plugin;
525
526     if (instance == NULL)
527         instance = name;
528
529     if ((plugin = find_plugin_instance(ctx, instance)) != NULL) {
530         if (instance == name || !strcmp(plugin->descriptor->name, name))
531             return TRUE;
532     }
533
534     return (mrp_load_plugin(ctx, name, instance, NULL, 0) != NULL);
535 }
536
537
538 int mrp_unload_plugin(mrp_plugin_t *plugin)
539 {
540     mrp_plugin_arg_t *pa, *da, *ra;
541     int               i, j;
542
543     if (plugin != NULL) {
544         if (plugin->refcnt == 0) {
545             mrp_list_delete(&plugin->hook);
546
547             pa = plugin->args;
548             da = plugin->descriptor->args;
549
550             if (pa != da) {
551                 for (i = 0; i < plugin->descriptor->narg; i++) {
552                     switch (pa->type) {
553                     case MRP_PLUGIN_ARG_TYPE_STRING:
554                         if (pa->str != da->str)
555                             mrp_free(pa->str);
556                         break;
557
558                     case MRP_PLUGIN_ARG_TYPE_OBJECT:
559                         mrp_json_unref(pa->obj.json);
560                         break;
561
562                     case MRP_PLUGIN_ARG_TYPE_UNDECL:
563                         ra = pa->rest.args;
564                         for (j = 0; j < pa->rest.narg; j++) {
565                             mrp_free(ra->key);
566                             switch (ra->type) {
567                             case MRP_PLUGIN_ARG_TYPE_STRING:
568                                 mrp_free(ra->str);
569                                 break;
570                             case MRP_PLUGIN_ARG_TYPE_OBJECT:
571                                 mrp_json_unref(ra->obj.json);
572                                 break;
573                             default:
574                                 break;
575                             }
576                             ra++;
577                         }
578                         break;
579                     default:
580                         break;
581                     }
582
583                     pa++;
584                     da++;
585                 }
586                 mrp_free(plugin->args);
587             }
588
589             plugin->descriptor->ninstance--;
590
591             emit_plugin_event(PLUGIN_EVENT_UNLOADED, plugin);
592
593             if (plugin->handle != NULL)
594                 dlclose(plugin->handle);
595
596             if (plugin->cmds != NULL) {
597                 mrp_console_del_group(plugin->ctx, plugin->cmds);
598
599                 mrp_free(plugin->cmds->name);
600                 mrp_free(plugin->cmds);
601             }
602
603             release_plugin_methods(plugin);
604             remove_plugin_methods(plugin);
605
606             mrp_free(plugin->instance);
607             mrp_free(plugin->path);
608             mrp_free(plugin);
609
610             return TRUE;
611         }
612         else
613             return FALSE;
614     }
615     else
616         return TRUE;
617 }
618
619
620 int mrp_start_plugins(mrp_context_t *ctx)
621 {
622     mrp_list_hook_t *p, *n;
623     mrp_plugin_t    *plugin;
624
625     mrp_list_foreach(&ctx->plugins, p, n) {
626         plugin = mrp_list_entry(p, typeof(*plugin), hook);
627
628         if (!import_plugin_methods(plugin)) {
629             if (!plugin->may_fail)
630                 return FALSE;
631             else
632                 goto unload;
633         }
634
635         if (!mrp_start_plugin(plugin)) {
636             if (!plugin->may_fail)
637                 return FALSE;
638             else {
639             unload:
640                 mrp_unload_plugin(plugin);
641             }
642         }
643
644         /* XXX TODO: argh, ugly kludge for plugins loading plugins... */
645         if (plugin->hook.next != n)
646             n = plugin->hook.next;
647     }
648
649     return TRUE;
650 }
651
652
653 int mrp_start_plugin(mrp_plugin_t *plugin)
654 {
655     if (plugin != NULL) {
656         if (plugin->state == MRP_PLUGIN_LOADED) {
657             if (!plugin->descriptor->init(plugin)) {
658                 mrp_log_error("Failed to start plugin %s (%s).",
659                               plugin->instance, plugin->descriptor->name);
660
661                 emit_plugin_event(PLUGIN_EVENT_FAILED, plugin);
662                 return FALSE;
663             }
664             else {
665                 plugin->state = MRP_PLUGIN_RUNNING;
666                 emit_plugin_event(PLUGIN_EVENT_STARTED, plugin);
667             }
668         }
669
670         return TRUE;
671     }
672     else
673         return FALSE;
674 }
675
676
677 int mrp_stop_plugin(mrp_plugin_t *plugin)
678 {
679     if (plugin != NULL) {
680         if (plugin->refcnt <= 1) {
681             emit_plugin_event(PLUGIN_EVENT_STOPPING, plugin);
682             plugin->descriptor->exit(plugin);
683             plugin->refcnt = 0;
684             plugin->state = MRP_PLUGIN_STOPPED;
685             emit_plugin_event(PLUGIN_EVENT_STOPPED, plugin);
686
687             return TRUE;
688         }
689         else
690             return FALSE;
691     }
692     else
693         return TRUE;
694 }
695
696
697 static mrp_plugin_t *find_plugin_instance(mrp_context_t *ctx,
698                                           const char *instance)
699 {
700     mrp_list_hook_t *p, *n;
701     mrp_plugin_t    *plg;
702
703     MRP_UNUSED(find_plugin);
704
705     mrp_list_foreach(&ctx->plugins, p, n) {
706         plg = mrp_list_entry(p, typeof(*plg), hook);
707
708         if (!strcmp(plg->instance, instance))
709             return plg;
710     }
711
712     return NULL;
713 }
714
715
716 static mrp_plugin_t *find_plugin(mrp_context_t *ctx, char *name)
717 {
718     mrp_list_hook_t *p, *n;
719     mrp_plugin_t    *plg;
720
721     mrp_list_foreach(&ctx->plugins, p, n) {
722         plg = mrp_list_entry(p, typeof(*plg), hook);
723
724         if (!strcmp(plg->descriptor->name, name))
725             return plg;
726     }
727
728     return NULL;
729 }
730
731
732 static mrp_plugin_descr_t *open_dynamic(mrp_context_t *ctx, const char *name,
733                                         void **handle)
734 {
735     mrp_plugin_descr_t *(*describe)(void);
736     mrp_plugin_descr_t   *d;
737     void                 *h;
738     char                  path[PATH_MAX];
739
740     snprintf(path, sizeof(path), "%s/%s%s.so", ctx->plugin_dir,
741              PLUGIN_PREFIX, name);
742
743     if (is_blacklisted(ctx, name, DYNAMIC))
744         return NULL;
745
746     if ((h = dlopen(path, RTLD_LAZY | RTLD_LOCAL)) != NULL) {
747         if ((describe = dlsym(h, "mrp_get_plugin_descriptor")) != NULL) {
748             if ((d = describe()) != NULL) {
749                 if (d->init != NULL && d->exit != NULL && d->name != NULL) {
750                     if (!d->core)
751                         *handle = h;
752                     else {
753                         *handle = dlopen(path,
754                                          RTLD_LAZY|RTLD_GLOBAL|RTLD_NOLOAD);
755                         dlclose(h);
756                     }
757
758                     return d;
759                 }
760                 else
761                     mrp_log_error("Ignoring plugin '%s' with invalid "
762                                   "plugin descriptor.", path);
763             }
764             else
765                 mrp_log_error("Plugin '%s' provided NULL descriptor.", path);
766         }
767         else
768             mrp_log_error("Plugin '%s' does not provide a descriptor.", path);
769     }
770     else {
771         if (access(path, F_OK) == 0) {
772             char *err = dlerror();
773
774             mrp_log_error("Failed to dlopen plugin '%s' (%s).", path,
775                           err ? err : "unknown error");
776         }
777     }
778
779     if (h != NULL)
780         dlclose(h);
781
782     *handle = NULL;
783     return NULL;
784 }
785
786
787 static mrp_plugin_descr_t *open_builtin(mrp_context_t *ctx, const char *name)
788 {
789     mrp_list_hook_t *p, *n;
790     mrp_plugin_t    *plugin;
791
792     mrp_list_foreach(&builtin_plugins, p, n) {
793         plugin = mrp_list_entry(p, typeof(*plugin), hook);
794
795         if (!strcmp(plugin->descriptor->name, name)) {
796 #ifdef __PARANOID_BLACKLIST_CHECK__
797             if (is_blacklisted(ctx, name, BUILTIN)) {
798                 mrp_log_warning("Hmm... blacklisted builtin %s still "
799                                 "reachable, blocking it.", name);
800                 return NULL;
801             }
802             else
803 #endif
804                 return plugin->descriptor;
805         }
806     }
807
808     return NULL;
809 }
810
811
812 static int parse_plugin_arg(mrp_plugin_arg_t *arg, mrp_plugin_arg_t *parg)
813 {
814     char        *end;
815     mrp_json_t **json;
816     char        *jstr;
817     int          jlen;
818
819     switch (parg->type) {
820     case MRP_PLUGIN_ARG_TYPE_STRING:
821         if (arg->str != NULL) {
822             parg->str = arg->str;
823             arg->str  = NULL;
824         }
825         return TRUE;
826
827     case MRP_PLUGIN_ARG_TYPE_BOOL:
828         if (arg->str != NULL) {
829             if (!strcasecmp(arg->str, "TRUE"))
830                 parg->bln = TRUE;
831             else if (!strcasecmp(arg->str, "FALSE"))
832                 parg->bln = FALSE;
833             else
834                 return FALSE;
835         }
836         else
837             parg->bln = TRUE;
838         return TRUE;
839
840     case MRP_PLUGIN_ARG_TYPE_UINT32:
841         parg->u32 = (uint32_t)strtoul(arg->str, &end, 0);
842         if (end && !*end)
843             return TRUE;
844         else
845             return FALSE;
846
847     case MRP_PLUGIN_ARG_TYPE_INT32:
848         parg->i32 = (int32_t)strtol(arg->str, &end, 0);
849         if (end && !*end)
850             return TRUE;
851         else
852             return FALSE;
853
854     case MRP_PLUGIN_ARG_TYPE_DOUBLE:
855         parg->dbl = strtod(arg->str, &end);
856         if (end && !*end)
857             return TRUE;
858         else
859             return FALSE;
860
861     case MRP_PLUGIN_ARG_TYPE_OBJECT:
862         jstr = arg->obj.str;
863         jlen = strlen(jstr);
864         json = &parg->obj.json;
865         if (mrp_json_parse_object(&jstr, &jlen, json) < 0 || jlen != 0)
866             return FALSE;
867         else
868             return TRUE;
869
870     default:
871         return FALSE;
872     }
873 }
874
875
876 static int parse_undeclared_arg(mrp_plugin_arg_t *arg, mrp_plugin_arg_t *pa)
877 {
878     mrp_plugin_arg_t *a;
879     char             *value, *end;
880     int               prfx;
881
882     if (mrp_reallocz(pa->rest.args, pa->rest.narg, pa->rest.narg + 1)) {
883         a = pa->rest.args + pa->rest.narg++;
884         a->key = mrp_strdup(arg->key);
885
886         if (a->key == NULL)
887             return FALSE;
888
889         if (arg->str == NULL) {
890             a->type = MRP_PLUGIN_ARG_TYPE_STRING;
891             a->str  = NULL;
892
893             return TRUE;
894         }
895
896         if (!strncmp(arg->str, "string:", 7)) {
897             value = arg->str + 7;
898         string:
899             a->type = MRP_PLUGIN_ARG_TYPE_STRING;
900             a->str  = mrp_strdup(value);
901
902             if (a->str != NULL)
903                 return TRUE;
904             else
905                 return FALSE;
906         }
907         else if (!strncmp(arg->str, "bool:", 5)) {
908             a->type = MRP_PLUGIN_ARG_TYPE_BOOL;
909             if      (!strcasecmp(arg->str + 5, "TRUE"))  a->bln = TRUE;
910             else if (!strcasecmp(arg->str + 5, "FALSE")) a->bln = FALSE;
911             else                                         return FALSE;
912         }
913         else if (!strncmp(arg->str, "int32:", 6)) {
914             a->type = MRP_PLUGIN_ARG_TYPE_INT32;
915             a->i32  = (int32_t)strtol(arg->str + 6, &end, 0);
916
917             if (end && !*end)
918                 return TRUE;
919             else
920                 return FALSE;
921         }
922         else if (!strncmp(arg->str, "uint32:", 7)) {
923             a->type = MRP_PLUGIN_ARG_TYPE_UINT32;
924             a->u32  = (uint32_t)strtoul(arg->str + 7, &end, 0);
925
926             if (end && !*end)
927                 return TRUE;
928             else
929                 return FALSE;
930         }
931         else if (!strncmp(arg->str, "double:", 7)) {
932             a->type = MRP_PLUGIN_ARG_TYPE_DOUBLE;
933             a->dbl  = strtod(arg->str + 7, &end);
934
935             if (end && !*end)
936                 return TRUE;
937             else
938                 return FALSE;
939         }
940         else if (!strncmp(arg->str, "object:", prfx=7) ||
941                  !strncmp(arg->str, "json:"  , prfx=5)) {
942             mrp_json_t **json = &a->obj.json;
943             char        *jstr = a->obj.str + prfx;
944             int          jlen = strlen(jstr);
945
946             if (mrp_json_parse_object(&jstr, &jlen, json) < 0 || jlen != 0)
947                 return FALSE;
948             else {
949                 a->type = MRP_PLUGIN_ARG_TYPE_OBJECT;
950                 return TRUE;
951             }
952         }
953         else {
954             if (!strcasecmp(arg->str, "TRUE") ||
955                 !strcasecmp(arg->str, "FALSE")) {
956                 a->type = MRP_PLUGIN_ARG_TYPE_BOOL;
957                 a->bln  = arg->str[0] == 't' || arg->str[0] == 'T';
958
959                 return TRUE;
960             }
961             if (arg->str[0] == '-' || arg->str[0] == '+' ||
962                 (arg->str[0] == '0' && arg->str[1] == 'x') ||
963                 ('0' <= arg->str[0] && arg->str[0] <= '9')) {
964                 a->i32 = strtol(arg->str, &end, 0);
965
966                 if (end && !*end) {
967                     a->type = MRP_PLUGIN_ARG_TYPE_INT32;
968                     return TRUE;
969                 }
970                 a->dbl = strtod(arg->str, &end);
971
972                 if (end && !*end) {
973                     a->type = MRP_PLUGIN_ARG_TYPE_DOUBLE;
974                     return TRUE;
975                 }
976             }
977             else {
978                 value = arg->str;
979                 goto string;
980             }
981         }
982     }
983
984     return FALSE;
985 }
986
987
988 static int parse_plugin_args(mrp_plugin_t *plugin,
989                              mrp_plugin_arg_t *argv, int argc)
990 {
991     mrp_plugin_descr_t *descr;
992     mrp_plugin_arg_t   *valid, *args, *pa, *a, *rest;
993     int                 i, j, cnt;
994
995     if (argv == NULL) {
996         plugin->args = plugin->descriptor->args;
997         return TRUE;
998     }
999
1000     descr = plugin->descriptor;
1001     valid = descr->args;
1002
1003     if (valid == NULL && argv != NULL) {
1004         mrp_log_error("Plugin '%s' (%s) does not take any arguments.",
1005                       plugin->instance, descr->name);
1006         return FALSE;
1007     }
1008
1009     if ((args = mrp_allocz_array(typeof(*args), descr->narg)) == NULL) {
1010         mrp_log_error("Failed to allocate arguments for plugin '%s'.",
1011                       plugin->instance);
1012         return FALSE;
1013     }
1014
1015     memcpy(args, descr->args, descr->narg * sizeof(*args));
1016     plugin->args = args;
1017
1018     for (i = 0, pa = plugin->args; i < descr->narg; i++, pa++) {
1019         if (pa->type == MRP_PLUGIN_ARG_TYPE_OBJECT) {
1020             mrp_json_t **json = &pa->obj.json;
1021             char        *jstr = pa->obj.str;
1022             int          jlen = strlen(jstr);
1023
1024             if (mrp_json_parse_object(&jstr, &jlen, json) < 0 || jlen != 0)
1025                 return FALSE;
1026         }
1027     }
1028
1029     rest = NULL;
1030     j    = 0;
1031     for (i = 0, a = argv; i < argc; i++, a++) {
1032         for (cnt = 0, pa = NULL; pa == NULL && cnt < descr->narg; cnt++) {
1033             if (args[j].type != MRP_PLUGIN_ARG_TYPE_UNDECL) {
1034                 if (!strcmp(a->key, args[j].key))
1035                     pa = args + j;
1036             }
1037             else
1038                 rest = args + j;
1039
1040             if (++j >= descr->narg)
1041                 j = 0;
1042         }
1043
1044         if (pa != NULL) {
1045             if (!parse_plugin_arg(a, pa)) {
1046                 mrp_log_error("Invalid argument '%s' for plugin '%s'.",
1047                               a->key, plugin->instance);
1048                 return FALSE;
1049             }
1050         }
1051         else if (rest != NULL) {
1052             if (!parse_undeclared_arg(a, rest)) {
1053                 mrp_log_error("Failed to parse argument '%s' for plugin '%s'.",
1054                               a->key, plugin->instance);
1055                 return FALSE;
1056             }
1057         }
1058         else {
1059             mrp_log_error("Plugin '%s' (%s) does not support argument '%s'",
1060                           plugin->instance, descr->name, a->key);
1061             return FALSE;
1062         }
1063     }
1064
1065     return TRUE;
1066 }
1067
1068
1069 mrp_plugin_arg_t *mrp_plugin_find_undecl_arg(mrp_plugin_arg_t *unspec,
1070                                              const char *key,
1071                                              mrp_plugin_arg_type_t type)
1072 {
1073     mrp_plugin_arg_t *arg;
1074     int               i;
1075
1076     for (i = 0, arg = unspec->rest.args; i < unspec->rest.narg; i++, arg++)
1077         if (!strcmp(arg->key, key) && (!type || type == arg->type))
1078             return arg;
1079
1080     return NULL;
1081 }
1082
1083
1084 static int export_plugin_methods(mrp_plugin_t *plugin)
1085 {
1086     mrp_method_descr_t *methods = plugin->descriptor->exports, *m;
1087     int                 nmethod = plugin->descriptor->nexport, i;
1088
1089     for (i = 0, m = methods; i < nmethod; i++, m++) {
1090         m->plugin = plugin;
1091         if (mrp_export_method(m) != 0) {
1092             mrp_log_error("Failed to export method %s from plugin %s.",
1093                           m->name, plugin->instance);
1094             return FALSE;
1095         }
1096     }
1097
1098     return TRUE;
1099 }
1100
1101
1102 static int remove_plugin_methods(mrp_plugin_t *plugin)
1103 {
1104     mrp_method_descr_t *methods = plugin->descriptor->exports, *m;
1105     int                 nmethod = plugin->descriptor->nexport, i;
1106     int                 success = TRUE;
1107
1108     for (i = 0, m = methods; i < nmethod; i++, m++) {
1109         m->plugin = plugin;
1110         if (mrp_remove_method(m) != 0) {
1111             mrp_log_error("Failed to remove exported method %s of plugin %s.",
1112                           m->name, plugin->instance);
1113             success = FALSE;
1114         }
1115     }
1116
1117     return success;
1118 }
1119
1120
1121 static int import_plugin_methods(mrp_plugin_t *plugin)
1122 {
1123     mrp_method_descr_t *methods = plugin->descriptor->imports, *m;
1124     int                 nmethod = plugin->descriptor->nimport, i;
1125
1126     for (i = 0, m = methods; i < nmethod; i++, m++) {
1127         if (mrp_import_method(m->name, m->signature,
1128                               (void **)m->native_ptr, NULL, NULL) != 0) {
1129             mrp_log_error("Failed to import method %s (%s) for plugin %s.",
1130                           m->name, m->signature, plugin->instance);
1131             return FALSE;
1132         }
1133     }
1134
1135     return TRUE;
1136 }
1137
1138
1139 static int release_plugin_methods(mrp_plugin_t *plugin)
1140 {
1141     mrp_method_descr_t *methods = plugin->descriptor->imports, *m;
1142     int                 nmethod = plugin->descriptor->nimport, i;
1143     int                 success = TRUE;
1144
1145     for (i = 0, m = methods; i < nmethod; i++, m++) {
1146         if (mrp_release_method(m->name, m->signature,
1147                                (void **)m->native_ptr, NULL) != 0) {
1148             mrp_log_error("Failed to release imported method %s of plugin %s.",
1149                           m->name, plugin->instance);
1150             success = FALSE;
1151         }
1152     }
1153
1154     return success;
1155 }