]> git.ipfire.org Git - thirdparty/strongswan.git/blame - src/libstrongswan/plugins/plugin_loader.c
Update copyright headers after acquisition by secunet
[thirdparty/strongswan.git] / src / libstrongswan / plugins / plugin_loader.c
CommitLineData
552cc11b 1/*
190a2788 2 * Copyright (C) 2010-2014 Tobias Brunner
552cc11b 3 * Copyright (C) 2007 Martin Willi
19ef2aec
TB
4 *
5 * Copyright (C) secunet Security Networks AG
552cc11b
MW
6 *
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the
9 * Free Software Foundation; either version 2 of the License, or (at your
10 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15 * for more details.
552cc11b
MW
16 */
17
eec675bf 18#define _GNU_SOURCE
552cc11b
MW
19#include "plugin_loader.h"
20
f2086e42
TB
21#include <sys/types.h>
22#include <sys/stat.h>
23#include <unistd.h>
a3d92a37 24#include <string.h>
2e6c203b 25#ifdef HAVE_DLADDR
552cc11b 26#include <dlfcn.h>
2e6c203b 27#endif
a3d92a37
MW
28#include <limits.h>
29#include <stdio.h>
552cc11b 30
f05b4272 31#include <utils/debug.h>
629fd2f4 32#include <library.h>
12642a68 33#include <collections/hashtable.h>
190a2788 34#include <collections/array.h>
12642a68 35#include <collections/linked_list.h>
552cc11b 36#include <plugins/plugin.h>
08944b68 37#include <utils/integrity_checker.h>
552cc11b
MW
38
39typedef struct private_plugin_loader_t private_plugin_loader_t;
49d7a98f
TB
40typedef struct registered_feature_t registered_feature_t;
41typedef struct provided_feature_t provided_feature_t;
62b9e2f9 42typedef struct plugin_entry_t plugin_entry_t;
552cc11b 43
1a06bf03
TB
44#ifdef STATIC_PLUGIN_CONSTRUCTORS
45/**
46 * Statically registered constructors
47 */
48static hashtable_t *plugin_constructors = NULL;
49#endif
50
552cc11b
MW
51/**
52 * private data of plugin_loader
53 */
54struct private_plugin_loader_t {
55
56 /**
57 * public functions
58 */
59 plugin_loader_t public;
7daf5226 60
552cc11b 61 /**
62b9e2f9 62 * List of plugins, as plugin_entry_t
552cc11b
MW
63 */
64 linked_list_t *plugins;
f1ba06c1 65
9eac6106 66 /**
49d7a98f 67 * Hashtable for registered features, as registered_feature_t
9eac6106 68 */
d9944102 69 hashlist_t *features;
49d7a98f
TB
70
71 /**
72 * Loaded features (stored in reverse order), as provided_feature_t
73 */
74 linked_list_t *loaded;
9eac6106 75
f2086e42
TB
76 /**
77 * List of paths to search for plugins
78 */
79 linked_list_t *paths;
80
f1ba06c1
TB
81 /**
82 * List of names of loaded plugins
83 */
84 char *loaded_plugins;
34ee14dd
TB
85
86 /**
87 * Statistics collected while loading features
88 */
89 struct {
90 /** Number of features that failed to load */
91 int failed;
92 /** Number of features that failed because of unmet dependencies */
93 int depends;
94 /** Number of features in critical plugins that failed to load */
95 int critical;
96 } stats;
2566eb21
TE
97
98 /**
99 * Fetch features from the given plugin, can optionally be overridden to
100 * modify feature arrays at loading time
101 */
102 int (*get_features)(plugin_t *plugin, plugin_feature_t *features[]);
552cc11b
MW
103};
104
49d7a98f
TB
105/**
106 * Registered plugin feature
107 */
108struct registered_feature_t {
109
110 /**
111 * The registered feature
112 */
113 plugin_feature_t *feature;
114
115 /**
116 * List of plugins providing this feature, as provided_feature_t
117 */
118 linked_list_t *plugins;
119};
120
121/**
122 * Hash a registered feature
123 */
ac4942c3 124static u_int registered_feature_hash(registered_feature_t *this)
49d7a98f
TB
125{
126 return plugin_feature_hash(this->feature);
127}
128
129/**
130 * Compare two registered features
131 */
132static bool registered_feature_equals(registered_feature_t *a,
133 registered_feature_t *b)
134{
135 return plugin_feature_equals(a->feature, b->feature);
136}
137
138/**
139 * Feature as provided by a plugin
140 */
141struct provided_feature_t {
142
143 /**
144 * Plugin providing the feature
145 */
146 plugin_entry_t *entry;
147
148 /**
149 * FEATURE_REGISTER or FEATURE_CALLBACK entry
150 */
151 plugin_feature_t *reg;
152
153 /**
154 * The provided feature (followed by dependencies)
155 */
156 plugin_feature_t *feature;
157
158 /**
159 * Maximum number of dependencies (following feature)
160 */
161 int dependencies;
162
163 /**
164 * TRUE if currently loading this feature (to prevent loops)
165 */
166 bool loading;
167
168 /**
169 * TRUE if feature loaded
170 */
171 bool loaded;
172
173 /**
174 * TRUE if feature failed to load
175 */
176 bool failed;
177};
178
62b9e2f9
MW
179/**
180 * Entry for a plugin
181 */
182struct plugin_entry_t {
183
184 /**
185 * Plugin instance
186 */
187 plugin_t *plugin;
188
e0712243
TB
189 /**
190 * TRUE, if the plugin is marked as critical
191 */
192 bool critical;
193
62b9e2f9
MW
194 /**
195 * dlopen handle, if in separate lib
196 */
197 void *handle;
198
199 /**
49d7a98f 200 * List of features, as provided_feature_t
a07f2a4b 201 */
49d7a98f 202 linked_list_t *features;
62b9e2f9
MW
203};
204
205/**
206 * Destroy a plugin entry
207 */
208static void plugin_entry_destroy(plugin_entry_t *entry)
209{
210 DESTROY_IF(entry->plugin);
211 if (entry->handle)
212 {
213 dlclose(entry->handle);
214 }
49d7a98f 215 entry->features->destroy(entry->features);
62b9e2f9
MW
216 free(entry);
217}
218
18d21a57
TB
219/**
220 * Wrapper for static plugin features
221 */
222typedef struct {
223
224 /**
225 * Implements plugin_t interface
226 */
227 plugin_t public;
228
229 /**
230 * Name of the module registering these features
231 */
232 char *name;
233
5421092b
MW
234 /**
235 * Optional reload function for features
236 */
237 bool (*reload)(void *data);
238
239 /**
240 * User data to pass to reload function
241 */
242 void *reload_data;
243
18d21a57
TB
244 /**
245 * Static plugin features
246 */
247 plugin_feature_t *features;
248
249 /**
250 * Number of plugin features
251 */
252 int count;
253
254} static_features_t;
255
256METHOD(plugin_t, get_static_name, char*,
257 static_features_t *this)
258{
259 return this->name;
260}
261
262METHOD(plugin_t, get_static_features, int,
263 static_features_t *this, plugin_feature_t *features[])
264{
265 *features = this->features;
266 return this->count;
267}
268
5421092b
MW
269METHOD(plugin_t, static_reload, bool,
270 static_features_t *this)
271{
272 if (this->reload)
273 {
274 return this->reload(this->reload_data);
275 }
276 return FALSE;
277}
278
18d21a57
TB
279METHOD(plugin_t, static_destroy, void,
280 static_features_t *this)
281{
282 free(this->features);
283 free(this->name);
284 free(this);
285}
286
287/**
288 * Create a wrapper around static plugin features.
289 */
290static plugin_t *static_features_create(const char *name,
5421092b
MW
291 plugin_feature_t features[], int count,
292 bool (*reload)(void*), void *reload_data)
18d21a57
TB
293{
294 static_features_t *this;
295
296 INIT(this,
297 .public = {
298 .get_name = _get_static_name,
299 .get_features = _get_static_features,
5421092b 300 .reload = _static_reload,
18d21a57
TB
301 .destroy = _static_destroy,
302 },
303 .name = strdup(name),
5421092b
MW
304 .reload = reload,
305 .reload_data = reload_data,
18d21a57
TB
306 .features = calloc(count, sizeof(plugin_feature_t)),
307 .count = count,
308 );
309
310 memcpy(this->features, features, sizeof(plugin_feature_t) * count);
311
312 return &this->public;
313}
314
1a06bf03
TB
315#ifdef STATIC_PLUGIN_CONSTRUCTORS
316/*
317 * Described in header.
318 */
319void plugin_constructor_register(char *name, void *constructor)
320{
321 bool old = FALSE;
322
323 if (lib && lib->leak_detective)
324 {
325 old = lib->leak_detective->set_state(lib->leak_detective, FALSE);
326 }
327
328 if (!plugin_constructors)
329 {
330 chunk_hash_seed();
331 plugin_constructors = hashtable_create(hashtable_hash_str,
332 hashtable_equals_str, 32);
333 }
334 if (constructor)
335 {
336 plugin_constructors->put(plugin_constructors, name, constructor);
337 }
338 else
339 {
340 plugin_constructors->remove(plugin_constructors, name);
341 if (!plugin_constructors->get_count(plugin_constructors))
342 {
343 plugin_constructors->destroy(plugin_constructors);
344 plugin_constructors = NULL;
345 }
346 }
347
348 if (lib && lib->leak_detective)
349 {
350 lib->leak_detective->set_state(lib->leak_detective, old);
351 }
352}
353#endif
354
3724668b 355/**
6302f497
TB
356 * create a plugin
357 * returns: NOT_FOUND, if the constructor was not found
358 * FAILED, if the plugin could not be constructed
3724668b 359 */
6302f497 360static status_t create_plugin(private_plugin_loader_t *this, void *handle,
af9c78d3
TB
361 char *name, char *create, bool integrity,
362 bool critical, plugin_entry_t **entry)
3724668b 363{
62b9e2f9 364 plugin_t *plugin;
1a06bf03 365 plugin_constructor_t constructor = NULL;
3724668b 366
1a06bf03
TB
367#ifdef STATIC_PLUGIN_CONSTRUCTORS
368 if (plugin_constructors)
369 {
370 constructor = plugin_constructors->get(plugin_constructors, name);
371 }
372 if (!constructor)
373#endif
374 {
375 constructor = dlsym(handle, create);
376 }
377 if (!constructor)
3724668b 378 {
6302f497
TB
379 return NOT_FOUND;
380 }
381 if (integrity && lib->integrity)
382 {
383 if (!lib->integrity->check_segment(lib->integrity, name, constructor))
384 {
385 DBG1(DBG_LIB, "plugin '%s': failed segment integrity test", name);
386 return FAILED;
387 }
388 DBG1(DBG_LIB, "plugin '%s': passed file and segment integrity tests",
389 name);
3724668b 390 }
62b9e2f9
MW
391 plugin = constructor();
392 if (plugin == NULL)
3724668b 393 {
8b0e0910
TB
394 DBG1(DBG_LIB, "plugin '%s': failed to load - %s returned NULL", name,
395 create);
6302f497 396 return FAILED;
3724668b 397 }
62b9e2f9
MW
398 INIT(*entry,
399 .plugin = plugin,
e0712243 400 .critical = critical,
49d7a98f 401 .features = linked_list_create(),
62b9e2f9 402 );
8b0e0910 403 DBG2(DBG_LIB, "plugin '%s': loaded successfully", name);
6302f497 404 return SUCCESS;
3724668b 405}
6302f497 406
552cc11b 407/**
a3d92a37 408 * load a single plugin
552cc11b 409 */
49d7a98f
TB
410static plugin_entry_t *load_plugin(private_plugin_loader_t *this, char *name,
411 char *file, bool critical)
552cc11b 412{
af9c78d3 413 char create[128];
62b9e2f9 414 plugin_entry_t *entry;
552cc11b 415 void *handle;
305c4aa8 416 int flag = RTLD_LAZY;
6302f497 417
af9c78d3
TB
418 if (snprintf(create, sizeof(create), "%s_plugin_create",
419 name) >= sizeof(create))
420 {
421 return NULL;
422 }
423 translate(create, "-", "_");
424 switch (create_plugin(this, RTLD_DEFAULT, name, create, FALSE, critical,
425 &entry))
6302f497
TB
426 {
427 case SUCCESS:
62b9e2f9 428 this->plugins->insert_last(this->plugins, entry);
49d7a98f 429 return entry;
6302f497 430 case NOT_FOUND:
a9f169f6
TB
431 if (file)
432 { /* try to load the plugin from a file */
433 break;
434 }
af9c78d3
TB
435 DBG1(DBG_LIB, "plugin '%s': failed to load - %s not found and no "
436 "plugin file available", name, create);
a9f169f6 437 /* fall-through */
6302f497 438 default:
49d7a98f 439 return NULL;
9ce567f8 440 }
6b04ba28 441 if (lib->integrity)
960e0c10 442 {
6b04ba28
AS
443 if (!lib->integrity->check_file(lib->integrity, name, file))
444 {
8b0e0910
TB
445 DBG1(DBG_LIB, "plugin '%s': failed file integrity test of '%s'",
446 name, file);
49d7a98f 447 return NULL;
6b04ba28 448 }
960e0c10 449 }
305c4aa8 450 if (lib->settings->get_bool(lib->settings, "%s.dlopen_use_rtld_now",
ddfb5dd4 451 FALSE, lib->ns))
305c4aa8
TB
452 {
453 flag = RTLD_NOW;
454 }
20a0fd92 455#ifdef RTLD_NODELETE
305c4aa8
TB
456 /* If supported, do not unload the library when unloading a plugin. It
457 * really doesn't matter in productive systems, but causes many (dependency)
458 * library reloads during unit tests. Some libraries can't handle that, e.g.
20a0fd92 459 * GnuTLS leaks file descriptors in its library load/unload functions. */
305c4aa8 460 flag |= RTLD_NODELETE;
20a0fd92 461#endif
305c4aa8 462 handle = dlopen(file, flag);
a3d92a37 463 if (handle == NULL)
552cc11b 464 {
c60b69e4 465 DBG1(DBG_LIB, "plugin '%s' failed to load: %s", name, dlerror());
49d7a98f 466 return NULL;
552cc11b 467 }
af9c78d3 468 switch (create_plugin(this, handle, name, create, TRUE, critical, &entry))
a3d92a37 469 {
af9c78d3
TB
470 case SUCCESS:
471 break;
472 case NOT_FOUND:
473 DBG1(DBG_LIB, "plugin '%s': failed to load - %s not found", name,
474 create);
475 /* fall-through */
476 default:
477 dlclose(handle);
478 return NULL;
a3d92a37 479 }
62b9e2f9
MW
480 entry->handle = handle;
481 this->plugins->insert_last(this->plugins, entry);
49d7a98f
TB
482 return entry;
483}
484
525cc46c
TB
485CALLBACK(feature_filter, bool,
486 void *null, enumerator_t *orig, va_list args)
49d7a98f 487{
525cc46c
TB
488 provided_feature_t *provided;
489 plugin_feature_t **feature;
490
491 VA_ARGS_VGET(args, feature);
492
493 while (orig->enumerate(orig, &provided))
494 {
495 if (provided->loaded)
496 {
497 *feature = provided->feature;
498 return TRUE;
499 }
500 }
501 return FALSE;
62b9e2f9
MW
502}
503
525cc46c
TB
504CALLBACK(plugin_filter, bool,
505 void *null, enumerator_t *orig, va_list args)
62b9e2f9 506{
525cc46c
TB
507 plugin_entry_t *entry;
508 linked_list_t **list;
509 plugin_t **plugin;
49d7a98f 510
525cc46c
TB
511 VA_ARGS_VGET(args, plugin, list);
512
513 if (orig->enumerate(orig, &entry))
fa7c8338 514 {
525cc46c
TB
515 *plugin = entry->plugin;
516 if (list)
517 {
518 enumerator_t *features;
519 features = enumerator_create_filter(
520 entry->features->create_enumerator(entry->features),
521 feature_filter, NULL, NULL);
522 *list = linked_list_create_from_enumerator(features);
523 }
524 return TRUE;
fa7c8338 525 }
525cc46c 526 return FALSE;
62b9e2f9
MW
527}
528
529METHOD(plugin_loader_t, create_plugin_enumerator, enumerator_t*,
530 private_plugin_loader_t *this)
531{
532 return enumerator_create_filter(
533 this->plugins->create_enumerator(this->plugins),
525cc46c 534 plugin_filter, NULL, NULL);
a3d92a37
MW
535}
536
2bedb0f2
MW
537METHOD(plugin_loader_t, has_feature, bool,
538 private_plugin_loader_t *this, plugin_feature_t feature)
539{
540 enumerator_t *plugins, *features;
541 plugin_t *plugin;
542 linked_list_t *list;
543 plugin_feature_t *current;
544 bool found = FALSE;
545
546 plugins = create_plugin_enumerator(this);
547 while (plugins->enumerate(plugins, &plugin, &list))
548 {
549 features = list->create_enumerator(list);
550 while (features->enumerate(features, &current))
551 {
552 if (plugin_feature_matches(&feature, current))
553 {
554 found = TRUE;
555 break;
556 }
557 }
558 features->destroy(features);
559 list->destroy(list);
560 }
561 plugins->destroy(plugins);
562
563 return found;
564}
565
f1ba06c1
TB
566/**
567 * Create a list of the names of all loaded plugins
568 */
569static char* loaded_plugins_list(private_plugin_loader_t *this)
570{
571 int buf_len = 128, len = 0;
572 char *buf, *name;
573 enumerator_t *enumerator;
574 plugin_t *plugin;
575
576 buf = malloc(buf_len);
577 buf[0] = '\0';
578 enumerator = create_plugin_enumerator(this);
579 while (enumerator->enumerate(enumerator, &plugin, NULL))
580 {
581 name = plugin->get_name(plugin);
582 if (len + (strlen(name) + 1) >= buf_len)
583 {
584 buf_len <<= 1;
585 buf = realloc(buf, buf_len);
586 }
587 len += snprintf(&buf[len], buf_len - len, "%s ", name);
588 }
589 enumerator->destroy(enumerator);
590 if (len > 0 && buf[len - 1] == ' ')
591 {
592 buf[len - 1] = '\0';
593 }
594 return buf;
595}
596
1535913a
MW
597/**
598 * Check if a plugin is already loaded
599 */
600static bool plugin_loaded(private_plugin_loader_t *this, char *name)
601{
602 enumerator_t *enumerator;
603 bool found = FALSE;
787b5884 604 plugin_t *plugin;
1535913a 605
62b9e2f9
MW
606 enumerator = create_plugin_enumerator(this);
607 while (enumerator->enumerate(enumerator, &plugin, NULL))
1535913a 608 {
787b5884 609 if (streq(plugin->get_name(plugin), name))
1535913a
MW
610 {
611 found = TRUE;
612 break;
613 }
614 }
615 enumerator->destroy(enumerator);
616 return found;
617}
618
62b9e2f9 619/**
49d7a98f
TB
620 * Forward declaration
621 */
622static void load_provided(private_plugin_loader_t *this,
623 provided_feature_t *provided,
624 int level);
625
2e4d110d
TB
626CALLBACK(is_feature_loaded, bool,
627 provided_feature_t *item, va_list args)
49d7a98f
TB
628{
629 return item->loaded;
630}
631
2e4d110d
TB
632CALLBACK(is_feature_loadable, bool,
633 provided_feature_t *item, va_list args)
62b9e2f9 634{
49d7a98f 635 return !item->loading && !item->loaded && !item->failed;
62b9e2f9
MW
636}
637
a07f2a4b 638/**
49d7a98f 639 * Find a loaded and matching feature
a07f2a4b 640 */
49d7a98f
TB
641static bool loaded_feature_matches(registered_feature_t *a,
642 registered_feature_t *b)
a07f2a4b 643{
49d7a98f
TB
644 if (plugin_feature_matches(a->feature, b->feature))
645 {
2e4d110d 646 return b->plugins->find_first(b->plugins, is_feature_loaded, NULL);
49d7a98f
TB
647 }
648 return FALSE;
a07f2a4b
MW
649}
650
62b9e2f9 651/**
49d7a98f 652 * Find a loadable module that equals the requested feature
62b9e2f9 653 */
49d7a98f
TB
654static bool loadable_feature_equals(registered_feature_t *a,
655 registered_feature_t *b)
62b9e2f9 656{
49d7a98f
TB
657 if (plugin_feature_equals(a->feature, b->feature))
658 {
2e4d110d 659 return b->plugins->find_first(b->plugins, is_feature_loadable, NULL);
49d7a98f
TB
660 }
661 return FALSE;
662}
663
664/**
665 * Find a loadable module that matches the requested feature
666 */
667static bool loadable_feature_matches(registered_feature_t *a,
668 registered_feature_t *b)
669{
670 if (plugin_feature_matches(a->feature, b->feature))
671 {
2e4d110d 672 return b->plugins->find_first(b->plugins, is_feature_loadable, NULL);
49d7a98f
TB
673 }
674 return FALSE;
675}
676
677/**
a6691450 678 * Returns a compatible plugin feature for the given dependency
49d7a98f
TB
679 */
680static bool find_compatible_feature(private_plugin_loader_t *this,
681 plugin_feature_t *dependency)
682{
683 registered_feature_t *feature, lookup = {
684 .feature = dependency,
685 };
686
687 feature = this->features->get_match(this->features, &lookup,
688 (void*)loaded_feature_matches);
689 return feature != NULL;
690}
691
692/**
693 * Load a registered plugin feature
694 */
695static void load_registered(private_plugin_loader_t *this,
696 registered_feature_t *registered,
697 int level)
698{
699 enumerator_t *enumerator;
700 provided_feature_t *provided;
701
702 enumerator = registered->plugins->create_enumerator(registered->plugins);
703 while (enumerator->enumerate(enumerator, &provided))
704 {
705 load_provided(this, provided, level);
706 }
707 enumerator->destroy(enumerator);
708}
709
710/**
711 * Try to load dependencies of the given feature
712 */
713static bool load_dependencies(private_plugin_loader_t *this,
714 provided_feature_t *provided,
715 int level)
716{
717 registered_feature_t *registered, lookup;
62b9e2f9
MW
718 int i;
719
720 /* first entry is provided feature, followed by dependencies */
49d7a98f 721 for (i = 1; i < provided->dependencies; i++)
62b9e2f9 722 {
49d7a98f
TB
723 if (provided->feature[i].kind != FEATURE_DEPENDS &&
724 provided->feature[i].kind != FEATURE_SDEPEND)
62b9e2f9
MW
725 { /* end of dependencies */
726 break;
727 }
49d7a98f
TB
728
729 /* we load the feature even if a compatible one is already loaded,
730 * otherwise e.g. a specific database implementation loaded before
731 * another might cause a plugin feature loaded in-between to fail */
732 lookup.feature = &provided->feature[i];
733 do
734 { /* prefer an exactly matching feature, could be omitted but
735 * results in a more predictable behavior */
736 registered = this->features->get_match(this->features,
737 &lookup,
738 (void*)loadable_feature_equals);
739 if (!registered)
740 { /* try fuzzy matching */
741 registered = this->features->get_match(this->features,
742 &lookup,
743 (void*)loadable_feature_matches);
744 }
745 if (registered)
62b9e2f9 746 {
49d7a98f
TB
747 load_registered(this, registered, level);
748 }
749 /* we could stop after finding one but for dependencies like
750 * DB_ANY it might be needed to load all matching features */
751 }
752 while (registered);
62b9e2f9 753
49d7a98f
TB
754 if (!find_compatible_feature(this, &provided->feature[i]))
755 {
49d7a98f
TB
756 bool soft = provided->feature[i].kind == FEATURE_SDEPEND;
757
3963dbbd
TB
758#ifndef USE_FUZZING
759 char *name, *provide, *depend;
ed13c60c 760 int indent = level * 2;
3963dbbd 761
49d7a98f
TB
762 name = provided->entry->plugin->get_name(provided->entry->plugin);
763 provide = plugin_feature_get_string(&provided->feature[0]);
764 depend = plugin_feature_get_string(&provided->feature[i]);
765 if (soft)
766 {
51b9d751 767 DBG3(DBG_LIB, "%*sfeature %s in plugin '%s' has unmet soft "
49d7a98f
TB
768 "dependency: %s", indent, "", provide, name, depend);
769 }
681e53c7
TB
770 else if (provided->entry->critical)
771 {
772 DBG1(DBG_LIB, "feature %s in critical plugin '%s' has unmet "
773 "dependency: %s", provide, name, depend);
774 }
49d7a98f
TB
775 else
776 {
51b9d751 777 DBG2(DBG_LIB, "feature %s in plugin '%s' has unmet dependency: "
49d7a98f
TB
778 "%s", provide, name, depend);
779 }
780 free(provide);
781 free(depend);
3963dbbd
TB
782#endif /* !USE_FUZZING */
783
49d7a98f
TB
784 if (soft)
785 { /* it's ok if we can't resolve soft dependencies */
786 continue;
62b9e2f9
MW
787 }
788 return FALSE;
789 }
790 }
791 return TRUE;
792}
793
794/**
49d7a98f 795 * Load registered plugin features
62b9e2f9 796 */
49d7a98f
TB
797static void load_feature(private_plugin_loader_t *this,
798 provided_feature_t *provided,
799 int level)
62b9e2f9 800{
49d7a98f 801 if (load_dependencies(this, provided, level))
62b9e2f9 802 {
49d7a98f
TB
803 if (plugin_feature_load(provided->entry->plugin, provided->feature,
804 provided->reg))
805 {
806 provided->loaded = TRUE;
807 /* insert first so we can unload the features in reverse order */
808 this->loaded->insert_first(this->loaded, provided);
34ee14dd
TB
809 return;
810 }
811
3963dbbd
TB
812#ifndef USE_FUZZING
813 char *name, *provide;
814
34ee14dd
TB
815 name = provided->entry->plugin->get_name(provided->entry->plugin);
816 provide = plugin_feature_get_string(&provided->feature[0]);
817 if (provided->entry->critical)
818 {
819 DBG1(DBG_LIB, "feature %s in critical plugin '%s' failed to load",
820 provide, name);
62b9e2f9 821 }
49d7a98f 822 else
62b9e2f9 823 {
34ee14dd
TB
824 DBG2(DBG_LIB, "feature %s in plugin '%s' failed to load",
825 provide, name);
62b9e2f9 826 }
34ee14dd 827 free(provide);
3963dbbd 828#endif /* !USE_FUZZING */
62b9e2f9 829 }
49d7a98f
TB
830 else
831 { /* TODO: we could check the current level and set a different flag when
832 * being loaded as dependency. If there are loops there is a chance the
833 * feature can be loaded later when loading the feature directly. */
34ee14dd 834 this->stats.depends++;
49d7a98f 835 }
34ee14dd
TB
836 provided->failed = TRUE;
837 this->stats.critical += provided->entry->critical ? 1 : 0;
838 this->stats.failed++;
62b9e2f9
MW
839}
840
841/**
49d7a98f 842 * Load a provided feature
62b9e2f9 843 */
49d7a98f
TB
844static void load_provided(private_plugin_loader_t *this,
845 provided_feature_t *provided,
846 int level)
62b9e2f9 847{
62b9e2f9 848
49d7a98f
TB
849 if (provided->loaded || provided->failed)
850 {
851 return;
852 }
3963dbbd
TB
853
854#ifndef USE_FUZZING
855 char *name, *provide;
ed13c60c 856 int indent = level * 2;
3963dbbd 857
49d7a98f
TB
858 name = provided->entry->plugin->get_name(provided->entry->plugin);
859 provide = plugin_feature_get_string(provided->feature);
860 if (provided->loading)
861 { /* prevent loop */
51b9d751 862 DBG3(DBG_LIB, "%*sloop detected while loading %s in plugin '%s'",
49d7a98f
TB
863 indent, "", provide, name);
864 free(provide);
865 return;
866 }
51b9d751 867 DBG3(DBG_LIB, "%*sloading feature %s in plugin '%s'",
49d7a98f
TB
868 indent, "", provide, name);
869 free(provide);
3963dbbd
TB
870#else
871 if (provided->loading)
872 {
873 return;
874 }
875#endif /* USE_FUZZING */
49d7a98f
TB
876
877 provided->loading = TRUE;
878 load_feature(this, provided, level + 1);
879 provided->loading = FALSE;
880}
881
882/**
883 * Load registered plugin features
884 */
885static void load_features(private_plugin_loader_t *this)
886{
887 enumerator_t *enumerator, *inner;
888 plugin_entry_t *plugin;
889 provided_feature_t *provided;
890
891 /* we do this in plugin order to allow implicit dependencies to be resolved
892 * by reordering plugins */
62b9e2f9 893 enumerator = this->plugins->create_enumerator(this->plugins);
49d7a98f 894 while (enumerator->enumerate(enumerator, &plugin))
62b9e2f9 895 {
49d7a98f
TB
896 inner = plugin->features->create_enumerator(plugin->features);
897 while (inner->enumerate(inner, &provided))
62b9e2f9 898 {
49d7a98f 899 load_provided(this, provided, 0);
40ca363a 900 }
49d7a98f 901 inner->destroy(inner);
62b9e2f9
MW
902 }
903 enumerator->destroy(enumerator);
62b9e2f9
MW
904}
905
2566eb21
TE
906/**
907 * Default implementation for plugin feature retrieval
908 */
909static int get_features_default(plugin_t *plugin, plugin_feature_t *features[])
910{
911 return plugin->get_features(plugin, features);
912}
913
d965872d 914/**
49d7a98f 915 * Register plugin features provided by the given plugin
d965872d 916 */
49d7a98f
TB
917static void register_features(private_plugin_loader_t *this,
918 plugin_entry_t *entry)
d965872d 919{
49d7a98f
TB
920 plugin_feature_t *feature, *reg;
921 registered_feature_t *registered, lookup;
922 provided_feature_t *provided;
923 int count, i;
d965872d 924
49d7a98f
TB
925 if (!entry->plugin->get_features)
926 { /* feature interface not supported */
927 DBG1(DBG_LIB, "plugin '%s' does not provide features, deprecated",
928 entry->plugin->get_name(entry->plugin));
929 return;
930 }
931 reg = NULL;
2566eb21 932 count = this->get_features(entry->plugin, &feature);
d965872d
MW
933 for (i = 0; i < count; i++)
934 {
935 switch (feature->kind)
936 {
937 case FEATURE_PROVIDE:
49d7a98f 938 lookup.feature = feature;
d9944102
TB
939 registered = this->features->ht.get(&this->features->ht,
940 &lookup);
49d7a98f 941 if (!registered)
d965872d 942 {
49d7a98f
TB
943 INIT(registered,
944 .feature = feature,
945 .plugins = linked_list_create(),
946 );
d9944102
TB
947 this->features->ht.put(&this->features->ht, registered,
948 registered);
d965872d 949 }
49d7a98f
TB
950 INIT(provided,
951 .entry = entry,
952 .feature = feature,
953 .reg = reg,
954 .dependencies = count - i,
955 );
956 registered->plugins->insert_last(registered->plugins,
957 provided);
958 entry->features->insert_last(entry->features, provided);
d965872d
MW
959 break;
960 case FEATURE_REGISTER:
961 case FEATURE_CALLBACK:
962 reg = feature;
963 break;
964 default:
965 break;
966 }
967 feature++;
968 }
49d7a98f
TB
969}
970
971/**
972 * Unregister a plugin feature
973 */
974static void unregister_feature(private_plugin_loader_t *this,
975 provided_feature_t *provided)
976{
977 registered_feature_t *registered, lookup;
978
979 lookup.feature = provided->feature;
d9944102 980 registered = this->features->ht.get(&this->features->ht, &lookup);
49d7a98f
TB
981 if (registered)
982 {
983 registered->plugins->remove(registered->plugins, provided, NULL);
984 if (registered->plugins->get_count(registered->plugins) == 0)
985 {
d9944102 986 this->features->ht.remove(&this->features->ht, &lookup);
49d7a98f
TB
987 registered->plugins->destroy(registered->plugins);
988 free(registered);
989 }
990 else if (registered->feature == provided->feature)
991 { /* update feature in case the providing plugin gets unloaded */
992 provided_feature_t *first;
993
994 registered->plugins->get_first(registered->plugins, (void**)&first);
995 registered->feature = first->feature;
996 }
997 }
998 free(provided);
999}
1000
1001/**
1002 * Unregister plugin features
1003 */
1004static void unregister_features(private_plugin_loader_t *this,
1005 plugin_entry_t *entry)
1006{
1007 provided_feature_t *provided;
1008 enumerator_t *enumerator;
1009
1010 enumerator = entry->features->create_enumerator(entry->features);
1011 while (enumerator->enumerate(enumerator, &provided))
1012 {
1013 entry->features->remove_at(entry->features, enumerator);
1014 unregister_feature(this, provided);
1015 }
1016 enumerator->destroy(enumerator);
d965872d
MW
1017}
1018
ef80de60 1019/**
49d7a98f 1020 * Remove plugins we were not able to load any plugin features from.
ef80de60
TB
1021 */
1022static void purge_plugins(private_plugin_loader_t *this)
1023{
1024 enumerator_t *enumerator;
1025 plugin_entry_t *entry;
1026
1027 enumerator = this->plugins->create_enumerator(this->plugins);
1028 while (enumerator->enumerate(enumerator, &entry))
1029 {
1030 if (!entry->plugin->get_features)
1031 { /* feature interface not supported */
1032 continue;
1033 }
2e4d110d
TB
1034 if (!entry->features->find_first(entry->features, is_feature_loaded,
1035 NULL))
ef80de60 1036 {
49d7a98f
TB
1037 DBG2(DBG_LIB, "unloading plugin '%s' without loaded features",
1038 entry->plugin->get_name(entry->plugin));
ef80de60 1039 this->plugins->remove_at(this->plugins, enumerator);
49d7a98f 1040 unregister_features(this, entry);
ef80de60
TB
1041 plugin_entry_destroy(entry);
1042 }
1043 }
1044 enumerator->destroy(enumerator);
1045}
1046
18d21a57
TB
1047METHOD(plugin_loader_t, add_static_features, void,
1048 private_plugin_loader_t *this, const char *name,
5421092b
MW
1049 plugin_feature_t features[], int count, bool critical,
1050 bool (*reload)(void*), void *reload_data)
18d21a57
TB
1051{
1052 plugin_entry_t *entry;
1053 plugin_t *plugin;
1054
5421092b 1055 plugin = static_features_create(name, features, count, reload, reload_data);
18d21a57
TB
1056
1057 INIT(entry,
1058 .plugin = plugin,
1059 .critical = critical,
49d7a98f 1060 .features = linked_list_create(),
18d21a57
TB
1061 );
1062 this->plugins->insert_last(this->plugins, entry);
49d7a98f 1063 register_features(this, entry);
18d21a57
TB
1064}
1065
f2086e42
TB
1066/**
1067 * Tries to find the plugin with the given name in the given path.
1068 */
1069static bool find_plugin(char *path, char *name, char *buf, char **file)
1070{
1071 struct stat stb;
1072
1073 if (path && snprintf(buf, PATH_MAX, "%s/libstrongswan-%s.so",
1074 path, name) < PATH_MAX)
1075 {
1076 if (stat(buf, &stb) == 0)
1077 {
1078 *file = buf;
1079 return TRUE;
1080 }
1081 }
1082 return FALSE;
1083}
1084
2e4d110d
TB
1085CALLBACK(find_plugin_cb, bool,
1086 char *path, va_list args)
1087{
1088 char *name, *buf, **file;
1089
1090 VA_ARGS_VGET(args, name, buf, file);
1091 return find_plugin(path, name, buf, file);
1092}
1093
190a2788
TB
1094/**
1095 * Used to sort plugins by priority
1096 */
1097typedef struct {
1098 /* name of the plugin */
1099 char *name;
1100 /* the plugins priority */
1101 int prio;
1102 /* default priority */
1103 int def;
1104} plugin_priority_t;
1105
1106static void plugin_priority_free(const plugin_priority_t *this, int idx,
1107 void *user)
1108{
1109 free(this->name);
1110}
1111
1112/**
1113 * Sort plugins and their priority by name
1114 */
1115static int plugin_priority_cmp_name(const plugin_priority_t *a,
1116 const plugin_priority_t *b)
1117{
1118 return strcmp(a->name, b->name);
1119}
1120
1121/**
1122 * Sort plugins by decreasing priority or default priority then by name
1123 */
1124static int plugin_priority_cmp(const plugin_priority_t *a,
1125 const plugin_priority_t *b, void *user)
1126{
1127 int diff;
1128
1129 diff = b->prio - a->prio;
1130 if (!diff)
1131 { /* the same priority, use default order */
1132 diff = b->def - a->def;
1133 if (!diff)
1134 { /* same default priority (i.e. both were not found in that list) */
1135 return strcmp(a->name, b->name);
1136 }
1137 }
1138 return diff;
1139}
1140
525cc46c
TB
1141CALLBACK(plugin_priority_filter, bool,
1142 void *null, enumerator_t *orig, va_list args)
7c81219b 1143{
525cc46c
TB
1144 plugin_priority_t *prio;
1145 char **name;
1146
1147 VA_ARGS_VGET(args, name);
1148
1149 if (orig->enumerate(orig, &prio))
1150 {
1151 *name = prio->name;
1152 return TRUE;
1153 }
1154 return FALSE;
7c81219b 1155}
190a2788
TB
1156
1157/**
1158 * Determine the list of plugins to load via load option in each plugin's
1159 * config section.
1160 */
1161static char *modular_pluginlist(char *list)
1162{
1163 enumerator_t *enumerator;
1164 array_t *given, *final;
1165 plugin_priority_t item, *current, found;
1166 char *plugin, *plugins = NULL;
1167 int i = 0, max_prio;
7c81219b 1168 bool load_def = FALSE;
190a2788
TB
1169
1170 given = array_create(sizeof(plugin_priority_t), 0);
1171 final = array_create(sizeof(plugin_priority_t), 0);
1172
1173 enumerator = enumerator_create_token(list, " ", " ");
1174 while (enumerator->enumerate(enumerator, &plugin))
1175 {
1176 item.name = strdup(plugin);
1177 item.prio = i++;
1178 array_insert(given, ARRAY_TAIL, &item);
1179 }
1180 enumerator->destroy(enumerator);
1181 array_sort(given, (void*)plugin_priority_cmp_name, NULL);
1182 /* the maximum priority used for plugins not found in this list */
1183 max_prio = i + 1;
1184
7c81219b
TB
1185 if (lib->settings->get_bool(lib->settings, "%s.load_modular", FALSE,
1186 lib->ns))
1187 {
1188 enumerator = lib->settings->create_section_enumerator(lib->settings,
190a2788 1189 "%s.plugins", lib->ns);
7c81219b
TB
1190 }
1191 else
1192 {
1193 enumerator = enumerator_create_filter(array_create_enumerator(given),
525cc46c 1194 plugin_priority_filter, NULL, NULL);
7c81219b
TB
1195 load_def = TRUE;
1196 }
190a2788
TB
1197 while (enumerator->enumerate(enumerator, &plugin))
1198 {
1199 item.prio = lib->settings->get_int(lib->settings,
7c81219b 1200 "%s.plugins.%s.load", 0, lib->ns, plugin);
190a2788
TB
1201 if (!item.prio)
1202 {
1203 if (!lib->settings->get_bool(lib->settings,
7c81219b 1204 "%s.plugins.%s.load", load_def, lib->ns, plugin))
190a2788
TB
1205 {
1206 continue;
1207 }
1208 item.prio = 1;
1209 }
1210 item.name = plugin;
1211 item.def = max_prio;
1212 if (array_bsearch(given, &item, (void*)plugin_priority_cmp_name,
1213 &found) != -1)
1214 {
1215 item.def = max_prio - found.prio;
1216 }
1217 array_insert(final, ARRAY_TAIL, &item);
1218 }
1219 enumerator->destroy(enumerator);
190a2788
TB
1220
1221 array_sort(final, (void*)plugin_priority_cmp, NULL);
1222
0ab7d5f1 1223 plugins = strdup("");
190a2788
TB
1224 enumerator = array_create_enumerator(final);
1225 while (enumerator->enumerate(enumerator, &current))
1226 {
1227 char *prev = plugins;
1228 if (asprintf(&plugins, "%s %s", plugins ?: "", current->name) < 0)
1229 {
1230 plugins = prev;
1231 break;
1232 }
1233 free(prev);
1234 }
1235 enumerator->destroy(enumerator);
7c81219b 1236 array_destroy_function(given, (void*)plugin_priority_free, NULL);
190a2788
TB
1237 array_destroy(final);
1238 return plugins;
1239}
1240
2b363425 1241METHOD(plugin_loader_t, load_plugins, bool,
b18a5317 1242 private_plugin_loader_t *this, char *list)
a3d92a37 1243{
11e85517 1244 enumerator_t *enumerator;
190a2788 1245 char *default_path = NULL, *plugins, *token;
d6a45127 1246 bool critical_failed = FALSE;
7daf5226 1247
a9f169f6 1248#ifdef PLUGINDIR
b18a5317 1249 default_path = PLUGINDIR;
a9f169f6 1250#endif /* PLUGINDIR */
5b03a350 1251
190a2788
TB
1252 plugins = modular_pluginlist(list);
1253
1254 enumerator = enumerator_create_token(plugins, " ", " ");
d6a45127 1255 while (!critical_failed && enumerator->enumerate(enumerator, &token))
a3d92a37 1256 {
49d7a98f 1257 plugin_entry_t *entry;
d6a45127 1258 bool critical = FALSE;
a9f169f6 1259 char buf[PATH_MAX], *file = NULL;
d6a45127 1260 int len;
7daf5226 1261
d6a45127
MW
1262 token = strdup(token);
1263 len = strlen(token);
1264 if (token[len-1] == '!')
1265 {
1266 critical = TRUE;
1267 token[len-1] = '\0';
1268 }
1535913a
MW
1269 if (plugin_loaded(this, token))
1270 {
1271 free(token);
1272 continue;
1273 }
f2086e42 1274 if (this->paths)
d6a45127 1275 {
2e4d110d
TB
1276 this->paths->find_first(this->paths, find_plugin_cb, NULL, token,
1277 buf, &file);
f2086e42
TB
1278 }
1279 if (!file)
1280 {
1281 find_plugin(default_path, token, buf, &file);
d6a45127 1282 }
49d7a98f
TB
1283 entry = load_plugin(this, token, file, critical);
1284 if (entry)
1285 {
1286 register_features(this, entry);
1287 }
1288 else if (critical)
d6a45127 1289 {
62b9e2f9
MW
1290 critical_failed = TRUE;
1291 DBG1(DBG_LIB, "loading critical plugin '%s' failed", token);
552cc11b 1292 }
787b5884 1293 free(token);
2feed2f3
MW
1294 }
1295 enumerator->destroy(enumerator);
1296 if (!critical_failed)
1297 {
49d7a98f 1298 load_features(this);
0d25c4ef 1299 if (this->stats.critical > 0)
34ee14dd 1300 {
0d25c4ef
TB
1301 critical_failed = TRUE;
1302 DBG1(DBG_LIB, "failed to load %d critical plugin feature%s",
1303 this->stats.critical, this->stats.critical == 1 ? "" : "s");
34ee14dd 1304 }
ef80de60
TB
1305 /* unload plugins that we were not able to load any features for */
1306 purge_plugins(this);
62b9e2f9 1307 }
f1ba06c1
TB
1308 if (!critical_failed)
1309 {
1310 free(this->loaded_plugins);
1311 this->loaded_plugins = loaded_plugins_list(this);
1312 }
190a2788
TB
1313 if (plugins != list)
1314 {
1315 free(plugins);
1316 }
d6a45127 1317 return !critical_failed;
552cc11b
MW
1318}
1319
49d7a98f
TB
1320/**
1321 * Unload plugin features, they are registered in reverse order
1322 */
1323static void unload_features(private_plugin_loader_t *this)
a3d92a37 1324{
d965872d 1325 enumerator_t *enumerator;
49d7a98f 1326 provided_feature_t *provided;
62b9e2f9 1327 plugin_entry_t *entry;
7daf5226 1328
49d7a98f
TB
1329 enumerator = this->loaded->create_enumerator(this->loaded);
1330 while (enumerator->enumerate(enumerator, &provided))
a3d92a37 1331 {
49d7a98f
TB
1332 entry = provided->entry;
1333 plugin_feature_unload(entry->plugin, provided->feature, provided->reg);
1334 this->loaded->remove_at(this->loaded, enumerator);
1335 entry->features->remove(entry->features, provided, NULL);
1336 unregister_feature(this, provided);
d965872d 1337 }
49d7a98f
TB
1338 enumerator->destroy(enumerator);
1339}
1340
1341METHOD(plugin_loader_t, unload, void,
1342 private_plugin_loader_t *this)
1343{
1344 plugin_entry_t *entry;
1345
1346 /* unload features followed by plugins, in reverse order */
1347 unload_features(this);
1348 while (this->plugins->remove_last(this->plugins, (void**)&entry) == SUCCESS)
d965872d 1349 {
49d7a98f
TB
1350 if (lib->leak_detective)
1351 { /* keep handle to report leaks properly */
1352 entry->handle = NULL;
62b9e2f9 1353 }
49d7a98f
TB
1354 unregister_features(this, entry);
1355 plugin_entry_destroy(entry);
a3d92a37 1356 }
f1ba06c1
TB
1357 free(this->loaded_plugins);
1358 this->loaded_plugins = NULL;
34ee14dd 1359 memset(&this->stats, 0, sizeof(this->stats));
a3d92a37
MW
1360}
1361
f2086e42
TB
1362METHOD(plugin_loader_t, add_path, void,
1363 private_plugin_loader_t *this, char *path)
1364{
1365 if (!this->paths)
1366 {
1367 this->paths = linked_list_create();
1368 }
1369 this->paths->insert_last(this->paths, strdupnull(path));
1370}
1371
ed49e9a3
MW
1372/**
1373 * Reload a plugin by name, NULL for all
1374 */
1375static u_int reload_by_name(private_plugin_loader_t *this, char *name)
1376{
1377 u_int reloaded = 0;
1378 enumerator_t *enumerator;
1379 plugin_t *plugin;
1380
1381 enumerator = create_plugin_enumerator(this);
62b9e2f9 1382 while (enumerator->enumerate(enumerator, &plugin, NULL))
ed49e9a3
MW
1383 {
1384 if (name == NULL || streq(name, plugin->get_name(plugin)))
1385 {
62b9e2f9 1386 if (plugin->reload && plugin->reload(plugin))
ed49e9a3
MW
1387 {
1388 DBG2(DBG_LIB, "reloaded configuration of '%s' plugin",
1389 plugin->get_name(plugin));
1390 reloaded++;
1391 }
1392 }
1393 }
1394 enumerator->destroy(enumerator);
1395 return reloaded;
1396}
1397
1398METHOD(plugin_loader_t, reload, u_int,
1399 private_plugin_loader_t *this, char *list)
1400{
1401 u_int reloaded = 0;
1402 enumerator_t *enumerator;
1403 char *name;
1404
1405 if (list == NULL)
1406 {
1407 return reload_by_name(this, NULL);
1408 }
1409 enumerator = enumerator_create_token(list, " ", "");
1410 while (enumerator->enumerate(enumerator, &name))
1411 {
1412 reloaded += reload_by_name(this, name);
1413 }
1414 enumerator->destroy(enumerator);
1415 return reloaded;
1416}
1417
ad1aaf4b
TB
1418METHOD(plugin_loader_t, loaded_plugins, char*,
1419 private_plugin_loader_t *this)
1420{
f1ba06c1 1421 return this->loaded_plugins ?: "";
ad1aaf4b
TB
1422}
1423
607f8e99
TB
1424METHOD(plugin_loader_t, status, void,
1425 private_plugin_loader_t *this, level_t level)
1426{
1427 if (this->loaded_plugins)
1428 {
1429 dbg(DBG_LIB, level, "loaded plugins: %s", this->loaded_plugins);
0d25c4ef
TB
1430
1431 if (this->stats.failed)
1432 {
1bfa5e0c
TB
1433 DBG2(DBG_LIB, "unable to load %d plugin feature%s (%d due to unmet "
1434 "dependencies)", this->stats.failed,
1435 this->stats.failed == 1 ? "" : "s", this->stats.depends);
0d25c4ef 1436 }
607f8e99
TB
1437 }
1438}
1439
2b363425
MW
1440METHOD(plugin_loader_t, destroy, void,
1441 private_plugin_loader_t *this)
552cc11b 1442{
62b9e2f9 1443 unload(this);
49d7a98f
TB
1444 this->features->destroy(this->features);
1445 this->loaded->destroy(this->loaded);
62b9e2f9 1446 this->plugins->destroy(this->plugins);
f2086e42 1447 DESTROY_FUNCTION_IF(this->paths, free);
f1ba06c1 1448 free(this->loaded_plugins);
552cc11b
MW
1449 free(this);
1450}
1451
1452/*
1453 * see header file
1454 */
1455plugin_loader_t *plugin_loader_create()
1456{
2b363425 1457 private_plugin_loader_t *this;
7daf5226 1458
2b363425
MW
1459 INIT(this,
1460 .public = {
18d21a57 1461 .add_static_features = _add_static_features,
6e279171 1462 .load = _load_plugins,
f2086e42 1463 .add_path = _add_path,
ed49e9a3 1464 .reload = _reload,
2b363425
MW
1465 .unload = _unload,
1466 .create_plugin_enumerator = _create_plugin_enumerator,
2bedb0f2 1467 .has_feature = _has_feature,
ad1aaf4b 1468 .loaded_plugins = _loaded_plugins,
607f8e99 1469 .status = _status,
2b363425
MW
1470 .destroy = _destroy,
1471 },
1472 .plugins = linked_list_create(),
49d7a98f 1473 .loaded = linked_list_create(),
d9944102 1474 .features = hashlist_create(
49d7a98f
TB
1475 (hashtable_hash_t)registered_feature_hash,
1476 (hashtable_equals_t)registered_feature_equals, 64),
2566eb21 1477 .get_features = dlsym(RTLD_DEFAULT, "plugin_loader_feature_filter"),
2b363425 1478 );
7daf5226 1479
2566eb21
TE
1480 if (!this->get_features)
1481 {
1482 this->get_features = get_features_default;
1483 }
1484
552cc11b
MW
1485 return &this->public;
1486}
8d2450d8
TB
1487
1488/*
1489 * See header
1490 */
1491void plugin_loader_add_plugindirs(char *basedir, char *plugins)
1492{
1493 enumerator_t *enumerator;
1494 char *name, path[PATH_MAX], dir[64];
1495
5078f87a 1496 enumerator = enumerator_create_token(plugins, " ", "!");
8d2450d8
TB
1497 while (enumerator->enumerate(enumerator, &name))
1498 {
1499 snprintf(dir, sizeof(dir), "%s", name);
1500 translate(dir, "-", "_");
1501 snprintf(path, sizeof(path), "%s/%s/.libs", basedir, dir);
1502 lib->plugins->add_path(lib->plugins, path);
1503 }
1504 enumerator->destroy(enumerator);
1505}