Use a hashtable to check for already loaded plugin features.
[strongswan.git] / src / libstrongswan / plugins / plugin_loader.c
1 /*
2 * Copyright (C) 2010-2012 Tobias Brunner
3 * Copyright (C) 2007 Martin Willi
4 * Hochschule fuer Technik Rapperswil
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; either version 2 of the License, or (at your
9 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * for more details.
15 */
16
17 #define _GNU_SOURCE
18 #include "plugin_loader.h"
19
20 #include <string.h>
21 #include <dlfcn.h>
22 #include <limits.h>
23 #include <stdio.h>
24
25 #include <debug.h>
26 #include <library.h>
27 #include <integrity_checker.h>
28 #include <utils/hashtable.h>
29 #include <utils/linked_list.h>
30 #include <plugins/plugin.h>
31
32 typedef struct private_plugin_loader_t private_plugin_loader_t;
33 typedef struct plugin_entry_t plugin_entry_t;
34
35 /**
36 * private data of plugin_loader
37 */
38 struct private_plugin_loader_t {
39
40 /**
41 * public functions
42 */
43 plugin_loader_t public;
44
45 /**
46 * List of plugins, as plugin_entry_t
47 */
48 linked_list_t *plugins;
49
50 /**
51 * Hashtable for loaded features, as plugin_feature_t
52 */
53 hashtable_t *loaded_features;
54
55 /**
56 * List of names of loaded plugins
57 */
58 char *loaded_plugins;
59 };
60
61 /**
62 * Entry for a plugin
63 */
64 struct plugin_entry_t {
65
66 /**
67 * Plugin instance
68 */
69 plugin_t *plugin;
70
71 /**
72 * dlopen handle, if in separate lib
73 */
74 void *handle;
75
76 /**
77 * List of loaded features
78 */
79 linked_list_t *loaded;
80
81 /**
82 * List features failed to load
83 */
84 linked_list_t *failed;
85 };
86
87 /**
88 * Destroy a plugin entry
89 */
90 static void plugin_entry_destroy(plugin_entry_t *entry)
91 {
92 DESTROY_IF(entry->plugin);
93 if (entry->handle)
94 {
95 dlclose(entry->handle);
96 }
97 entry->loaded->destroy(entry->loaded);
98 entry->failed->destroy(entry->failed);
99 free(entry);
100 }
101
102 /**
103 * create a plugin
104 * returns: NOT_FOUND, if the constructor was not found
105 * FAILED, if the plugin could not be constructed
106 */
107 static status_t create_plugin(private_plugin_loader_t *this, void *handle,
108 char *name, bool integrity, plugin_entry_t **entry)
109 {
110 char create[128];
111 plugin_t *plugin;
112 plugin_constructor_t constructor;
113
114 if (snprintf(create, sizeof(create), "%s_plugin_create",
115 name) >= sizeof(create))
116 {
117 return FAILED;
118 }
119 translate(create, "-", "_");
120 constructor = dlsym(handle, create);
121 if (constructor == NULL)
122 {
123 return NOT_FOUND;
124 }
125 if (integrity && lib->integrity)
126 {
127 if (!lib->integrity->check_segment(lib->integrity, name, constructor))
128 {
129 DBG1(DBG_LIB, "plugin '%s': failed segment integrity test", name);
130 return FAILED;
131 }
132 DBG1(DBG_LIB, "plugin '%s': passed file and segment integrity tests",
133 name);
134 }
135 plugin = constructor();
136 if (plugin == NULL)
137 {
138 DBG1(DBG_LIB, "plugin '%s': failed to load - %s returned NULL", name,
139 create);
140 return FAILED;
141 }
142 INIT(*entry,
143 .plugin = plugin,
144 .loaded = linked_list_create(),
145 .failed = linked_list_create(),
146 );
147 DBG2(DBG_LIB, "plugin '%s': loaded successfully", name);
148 return SUCCESS;
149 }
150
151 /**
152 * load a single plugin
153 */
154 static bool load_plugin(private_plugin_loader_t *this, char *name, char *file)
155 {
156 plugin_entry_t *entry;
157 void *handle;
158
159 switch (create_plugin(this, RTLD_DEFAULT, name, FALSE, &entry))
160 {
161 case SUCCESS:
162 this->plugins->insert_last(this->plugins, entry);
163 return TRUE;
164 case NOT_FOUND:
165 /* try to load the plugin from a file */
166 break;
167 default:
168 return FALSE;
169 }
170 if (lib->integrity)
171 {
172 if (!lib->integrity->check_file(lib->integrity, name, file))
173 {
174 DBG1(DBG_LIB, "plugin '%s': failed file integrity test of '%s'",
175 name, file);
176 return FALSE;
177 }
178 }
179 handle = dlopen(file, RTLD_LAZY);
180 if (handle == NULL)
181 {
182 DBG1(DBG_LIB, "plugin '%s' failed to load: %s", name, dlerror());
183 return FALSE;
184 }
185 if (create_plugin(this, handle, name, TRUE, &entry) != SUCCESS)
186 {
187 dlclose(handle);
188 return FALSE;
189 }
190 entry->handle = handle;
191 this->plugins->insert_last(this->plugins, entry);
192 return TRUE;
193 }
194
195 /**
196 * Convert enumerated entries to plugin_t
197 */
198 static bool plugin_filter(void *null, plugin_entry_t **entry, plugin_t **plugin,
199 void *in, linked_list_t **list)
200 {
201 *plugin = (*entry)->plugin;
202 if (list)
203 {
204 *list = (*entry)->loaded;
205 }
206 return TRUE;
207 }
208
209 METHOD(plugin_loader_t, create_plugin_enumerator, enumerator_t*,
210 private_plugin_loader_t *this)
211 {
212 return enumerator_create_filter(
213 this->plugins->create_enumerator(this->plugins),
214 (void*)plugin_filter, NULL, NULL);
215 }
216
217 /**
218 * Create a list of the names of all loaded plugins
219 */
220 static char* loaded_plugins_list(private_plugin_loader_t *this)
221 {
222 int buf_len = 128, len = 0;
223 char *buf, *name;
224 enumerator_t *enumerator;
225 plugin_t *plugin;
226
227 buf = malloc(buf_len);
228 buf[0] = '\0';
229 enumerator = create_plugin_enumerator(this);
230 while (enumerator->enumerate(enumerator, &plugin, NULL))
231 {
232 name = plugin->get_name(plugin);
233 if (len + (strlen(name) + 1) >= buf_len)
234 {
235 buf_len <<= 1;
236 buf = realloc(buf, buf_len);
237 }
238 len += snprintf(&buf[len], buf_len - len, "%s ", name);
239 }
240 enumerator->destroy(enumerator);
241 if (len > 0 && buf[len - 1] == ' ')
242 {
243 buf[len - 1] = '\0';
244 }
245 return buf;
246 }
247
248
249 /**
250 * Check if a plugin is already loaded
251 */
252 static bool plugin_loaded(private_plugin_loader_t *this, char *name)
253 {
254 enumerator_t *enumerator;
255 bool found = FALSE;
256 plugin_t *plugin;
257
258 enumerator = create_plugin_enumerator(this);
259 while (enumerator->enumerate(enumerator, &plugin, NULL))
260 {
261 if (streq(plugin->get_name(plugin), name))
262 {
263 found = TRUE;
264 break;
265 }
266 }
267 enumerator->destroy(enumerator);
268 return found;
269 }
270
271 /**
272 * Check if a feature of a plugin is already loaded
273 */
274 static bool feature_loaded(private_plugin_loader_t *this, plugin_entry_t *entry,
275 plugin_feature_t *feature)
276 {
277 return entry->loaded->find_first(entry->loaded, NULL,
278 (void**)&feature) == SUCCESS;
279 }
280
281 /**
282 * Check if loading a feature of a plugin failed
283 */
284 static bool feature_failed(private_plugin_loader_t *this, plugin_entry_t *entry,
285 plugin_feature_t *feature)
286 {
287 return entry->failed->find_first(entry->failed, NULL,
288 (void**)&feature) == SUCCESS;
289 }
290
291 /**
292 * Check if dependencies are satisfied
293 */
294 static bool dependencies_satisfied(private_plugin_loader_t *this,
295 plugin_entry_t *entry, bool soft, bool report,
296 plugin_feature_t *features, int count)
297 {
298 int i;
299
300 /* first entry is provided feature, followed by dependencies */
301 for (i = 1; i < count; i++)
302 {
303 plugin_feature_t *found;
304
305 if (features[i].kind != FEATURE_DEPENDS &&
306 features[i].kind != FEATURE_SDEPEND)
307 { /* end of dependencies */
308 break;
309 }
310 found = this->loaded_features->get(this->loaded_features, &features[i]);
311 if (!found && (features[i].kind != FEATURE_SDEPEND || soft))
312 {
313 if (report)
314 {
315 char *provide, *depend, *name;
316
317 name = entry->plugin->get_name(entry->plugin);
318 provide = plugin_feature_get_string(&features[0]);
319 depend = plugin_feature_get_string(&features[i]);
320 DBG2(DBG_LIB, "feature %s in '%s' plugin has unsatisfied "
321 "dependency: %s", provide, name, depend);
322 free(provide);
323 free(depend);
324 }
325 return FALSE;
326 }
327 }
328 return TRUE;
329 }
330
331 /**
332 * Check if a given feature is still required as dependency
333 */
334 static bool dependency_required(private_plugin_loader_t *this,
335 plugin_feature_t *dep)
336 {
337 enumerator_t *enumerator;
338 plugin_feature_t *features;
339 plugin_entry_t *entry;
340 int count, i;
341
342 enumerator = this->plugins->create_enumerator(this->plugins);
343 while (enumerator->enumerate(enumerator, &entry))
344 {
345 if (!entry->plugin->get_features)
346 { /* features not supported */
347 continue;
348 }
349 count = entry->plugin->get_features(entry->plugin, &features);
350 for (i = 0; i < count; i++)
351 {
352 if (&features[i] != dep &&
353 feature_loaded(this, entry, &features[i]))
354 {
355 while (++i < count && (features[i].kind == FEATURE_DEPENDS ||
356 features[i].kind == FEATURE_SDEPEND))
357 {
358 if (plugin_feature_matches(&features[i], dep))
359 {
360 enumerator->destroy(enumerator);
361 return TRUE;
362 }
363 }
364 }
365 }
366 }
367 enumerator->destroy(enumerator);
368 return FALSE;
369 }
370
371 /**
372 * Load plugin features in correct order
373 */
374 static int load_features(private_plugin_loader_t *this, bool soft, bool report)
375 {
376 enumerator_t *enumerator;
377 plugin_feature_t *feature, *reg;
378 plugin_entry_t *entry;
379 int count, i, loaded = 0;
380
381 enumerator = this->plugins->create_enumerator(this->plugins);
382 while (enumerator->enumerate(enumerator, &entry))
383 {
384 if (!entry->plugin->get_features)
385 { /* feature interface not supported */
386 continue;
387 }
388 reg = NULL;
389 count = entry->plugin->get_features(entry->plugin, &feature);
390 for (i = 0; i < count; i++)
391 {
392 switch (feature->kind)
393 {
394 case FEATURE_PROVIDE:
395 if (!feature_loaded(this, entry, feature) &&
396 !feature_failed(this, entry, feature) &&
397 dependencies_satisfied(this, entry, soft, report,
398 feature, count - i))
399 {
400 if (plugin_feature_load(entry->plugin, feature, reg))
401 {
402 this->loaded_features->put(this->loaded_features,
403 feature, feature);
404 entry->loaded->insert_last(entry->loaded, feature);
405 loaded++;
406 }
407 else
408 {
409 entry->failed->insert_last(entry->failed, feature);
410 }
411 }
412 break;
413 case FEATURE_REGISTER:
414 case FEATURE_CALLBACK:
415 reg = feature;
416 break;
417 default:
418 break;
419 }
420 feature++;
421 }
422 if (loaded && !report)
423 { /* got new feature, restart from beginning of list */
424 break;
425 }
426 }
427 enumerator->destroy(enumerator);
428 return loaded;
429 }
430
431 /**
432 * Since plugin_feature_t objectss are loosely matched we can't use remove() to
433 * remove them from the hashtable.
434 */
435 static void unregister_loaded_feature(private_plugin_loader_t *this,
436 plugin_feature_t *feature)
437 {
438 plugin_feature_t *current;
439 enumerator_t *enumerator;
440
441 enumerator = this->loaded_features->create_enumerator(this->loaded_features);
442 while (enumerator->enumerate(enumerator, &current, NULL))
443 {
444 if (current == feature)
445 {
446 this->loaded_features->remove_at(this->loaded_features, enumerator);
447 break;
448 }
449 }
450 enumerator->destroy(enumerator);
451 }
452
453 /**
454 * Try to unload plugin features on which is not depended anymore
455 */
456 static int unload_features(private_plugin_loader_t *this, plugin_entry_t *entry)
457 {
458 plugin_feature_t *feature, *reg = NULL;
459 int count, i, unloaded = 0;
460
461 count = entry->plugin->get_features(entry->plugin, &feature);
462 for (i = 0; i < count; i++)
463 {
464 switch (feature->kind)
465 {
466 case FEATURE_PROVIDE:
467 if (feature_loaded(this, entry, feature) &&
468 !dependency_required(this, feature) &&
469 plugin_feature_unload(entry->plugin, feature, reg))
470 {
471 unregister_loaded_feature(this, feature);
472 entry->loaded->remove(entry->loaded, feature, NULL);
473 unloaded++;
474 }
475 break;
476 case FEATURE_REGISTER:
477 case FEATURE_CALLBACK:
478 reg = feature;
479 break;
480 default:
481 break;
482 }
483 feature++;
484 }
485 return unloaded;
486 }
487
488 /**
489 * Remove plugins that we were not able to load any features from.
490 */
491 static void purge_plugins(private_plugin_loader_t *this)
492 {
493 enumerator_t *enumerator;
494 plugin_entry_t *entry;
495
496 enumerator = this->plugins->create_enumerator(this->plugins);
497 while (enumerator->enumerate(enumerator, &entry))
498 {
499 if (!entry->plugin->get_features)
500 { /* feature interface not supported */
501 continue;
502 }
503 if (!entry->loaded->get_count(entry->loaded))
504 {
505 this->plugins->remove_at(this->plugins, enumerator);
506 plugin_entry_destroy(entry);
507 }
508 }
509 enumerator->destroy(enumerator);
510 }
511
512 METHOD(plugin_loader_t, load_plugins, bool,
513 private_plugin_loader_t *this, char *path, char *list)
514 {
515 enumerator_t *enumerator;
516 char *token;
517 bool critical_failed = FALSE;
518
519 if (path == NULL)
520 {
521 path = PLUGINDIR;
522 }
523
524 enumerator = enumerator_create_token(list, " ", " ");
525 while (!critical_failed && enumerator->enumerate(enumerator, &token))
526 {
527 bool critical = FALSE;
528 char file[PATH_MAX];
529 int len;
530
531 token = strdup(token);
532 len = strlen(token);
533 if (token[len-1] == '!')
534 {
535 critical = TRUE;
536 token[len-1] = '\0';
537 }
538 if (plugin_loaded(this, token))
539 {
540 free(token);
541 continue;
542 }
543 if (snprintf(file, sizeof(file), "%s/libstrongswan-%s.so",
544 path, token) >= sizeof(file))
545 {
546 return NULL;
547 }
548 if (!load_plugin(this, token, file) && critical)
549 {
550 critical_failed = TRUE;
551 DBG1(DBG_LIB, "loading critical plugin '%s' failed", token);
552 }
553 free(token);
554 /* TODO: we currently load features after each plugin is loaded. This
555 * will not be necessary once we have features support in all plugins.
556 */
557 while (load_features(this, TRUE, FALSE))
558 {
559 /* try load new features until we don't get new ones */
560 }
561 }
562 enumerator->destroy(enumerator);
563 if (!critical_failed)
564 {
565 while (load_features(this, FALSE, FALSE))
566 {
567 /* enforce loading features, ignoring soft dependencies */
568 }
569 /* report missing dependencies */
570 load_features(this, FALSE, TRUE);
571 /* unload plugins that we were not able to load any features for */
572 purge_plugins(this);
573 }
574 if (!critical_failed)
575 {
576 free(this->loaded_plugins);
577 this->loaded_plugins = loaded_plugins_list(this);
578 }
579 return !critical_failed;
580 }
581
582 METHOD(plugin_loader_t, unload, void,
583 private_plugin_loader_t *this)
584 {
585 enumerator_t *enumerator;
586 plugin_entry_t *entry;
587 linked_list_t *list;
588
589 /* unload plugins in reverse order, for those not supporting features */
590 list = linked_list_create();
591 while (this->plugins->remove_last(this->plugins, (void**)&entry) == SUCCESS)
592 {
593 list->insert_last(list, entry);
594 }
595 while (list->remove_last(list, (void**)&entry) == SUCCESS)
596 {
597 this->plugins->insert_first(this->plugins, entry);
598 }
599 list->destroy(list);
600 while (this->plugins->get_count(this->plugins))
601 {
602 enumerator = this->plugins->create_enumerator(this->plugins);
603 while (enumerator->enumerate(enumerator, &entry))
604 {
605 if (entry->plugin->get_features)
606 { /* supports features */
607 while (unload_features(this, entry));
608 }
609 if (entry->loaded->get_count(entry->loaded) == 0)
610 {
611 if (lib->leak_detective)
612 { /* keep handle to report leaks properly */
613 entry->handle = NULL;
614 }
615 this->plugins->remove_at(this->plugins, enumerator);
616 plugin_entry_destroy(entry);
617 }
618 }
619 enumerator->destroy(enumerator);
620 }
621 free(this->loaded_plugins);
622 this->loaded_plugins = NULL;
623 }
624
625 /**
626 * Reload a plugin by name, NULL for all
627 */
628 static u_int reload_by_name(private_plugin_loader_t *this, char *name)
629 {
630 u_int reloaded = 0;
631 enumerator_t *enumerator;
632 plugin_t *plugin;
633
634 enumerator = create_plugin_enumerator(this);
635 while (enumerator->enumerate(enumerator, &plugin, NULL))
636 {
637 if (name == NULL || streq(name, plugin->get_name(plugin)))
638 {
639 if (plugin->reload && plugin->reload(plugin))
640 {
641 DBG2(DBG_LIB, "reloaded configuration of '%s' plugin",
642 plugin->get_name(plugin));
643 reloaded++;
644 }
645 }
646 }
647 enumerator->destroy(enumerator);
648 return reloaded;
649 }
650
651 METHOD(plugin_loader_t, reload, u_int,
652 private_plugin_loader_t *this, char *list)
653 {
654 u_int reloaded = 0;
655 enumerator_t *enumerator;
656 char *name;
657
658 if (list == NULL)
659 {
660 return reload_by_name(this, NULL);
661 }
662 enumerator = enumerator_create_token(list, " ", "");
663 while (enumerator->enumerate(enumerator, &name))
664 {
665 reloaded += reload_by_name(this, name);
666 }
667 enumerator->destroy(enumerator);
668 return reloaded;
669 }
670
671 METHOD(plugin_loader_t, loaded_plugins, char*,
672 private_plugin_loader_t *this)
673 {
674 return this->loaded_plugins ?: "";
675 }
676
677 METHOD(plugin_loader_t, destroy, void,
678 private_plugin_loader_t *this)
679 {
680 unload(this);
681 this->loaded_features->destroy(this->loaded_features);
682 this->plugins->destroy(this->plugins);
683 free(this->loaded_plugins);
684 free(this);
685 }
686
687 /*
688 * see header file
689 */
690 plugin_loader_t *plugin_loader_create()
691 {
692 private_plugin_loader_t *this;
693
694 INIT(this,
695 .public = {
696 .load = _load_plugins,
697 .reload = _reload,
698 .unload = _unload,
699 .create_plugin_enumerator = _create_plugin_enumerator,
700 .loaded_plugins = _loaded_plugins,
701 .destroy = _destroy,
702 },
703 .plugins = linked_list_create(),
704 .loaded_features = hashtable_create(
705 (hashtable_hash_t)plugin_feature_hash,
706 (hashtable_equals_t)plugin_feature_matches, 64),
707 );
708
709 return &this->public;
710 }
711