aabeb1f86c0c7b41e1f8dc131ac55d2f4cdfe6be
[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 * Compare function for hashtable of loaded features.
104 */
105 static bool plugin_feature_equals(plugin_feature_t *a, plugin_feature_t *b)
106 {
107 return a == b;
108 }
109
110 /**
111 * create a plugin
112 * returns: NOT_FOUND, if the constructor was not found
113 * FAILED, if the plugin could not be constructed
114 */
115 static status_t create_plugin(private_plugin_loader_t *this, void *handle,
116 char *name, bool integrity, plugin_entry_t **entry)
117 {
118 char create[128];
119 plugin_t *plugin;
120 plugin_constructor_t constructor;
121
122 if (snprintf(create, sizeof(create), "%s_plugin_create",
123 name) >= sizeof(create))
124 {
125 return FAILED;
126 }
127 translate(create, "-", "_");
128 constructor = dlsym(handle, create);
129 if (constructor == NULL)
130 {
131 return NOT_FOUND;
132 }
133 if (integrity && lib->integrity)
134 {
135 if (!lib->integrity->check_segment(lib->integrity, name, constructor))
136 {
137 DBG1(DBG_LIB, "plugin '%s': failed segment integrity test", name);
138 return FAILED;
139 }
140 DBG1(DBG_LIB, "plugin '%s': passed file and segment integrity tests",
141 name);
142 }
143 plugin = constructor();
144 if (plugin == NULL)
145 {
146 DBG1(DBG_LIB, "plugin '%s': failed to load - %s returned NULL", name,
147 create);
148 return FAILED;
149 }
150 INIT(*entry,
151 .plugin = plugin,
152 .loaded = linked_list_create(),
153 .failed = linked_list_create(),
154 );
155 DBG2(DBG_LIB, "plugin '%s': loaded successfully", name);
156 return SUCCESS;
157 }
158
159 /**
160 * load a single plugin
161 */
162 static bool load_plugin(private_plugin_loader_t *this, char *name, char *file)
163 {
164 plugin_entry_t *entry;
165 void *handle;
166
167 switch (create_plugin(this, RTLD_DEFAULT, name, FALSE, &entry))
168 {
169 case SUCCESS:
170 this->plugins->insert_last(this->plugins, entry);
171 return TRUE;
172 case NOT_FOUND:
173 /* try to load the plugin from a file */
174 break;
175 default:
176 return FALSE;
177 }
178 if (lib->integrity)
179 {
180 if (!lib->integrity->check_file(lib->integrity, name, file))
181 {
182 DBG1(DBG_LIB, "plugin '%s': failed file integrity test of '%s'",
183 name, file);
184 return FALSE;
185 }
186 }
187 handle = dlopen(file, RTLD_LAZY);
188 if (handle == NULL)
189 {
190 DBG1(DBG_LIB, "plugin '%s' failed to load: %s", name, dlerror());
191 return FALSE;
192 }
193 if (create_plugin(this, handle, name, TRUE, &entry) != SUCCESS)
194 {
195 dlclose(handle);
196 return FALSE;
197 }
198 entry->handle = handle;
199 this->plugins->insert_last(this->plugins, entry);
200 return TRUE;
201 }
202
203 /**
204 * Convert enumerated entries to plugin_t
205 */
206 static bool plugin_filter(void *null, plugin_entry_t **entry, plugin_t **plugin,
207 void *in, linked_list_t **list)
208 {
209 *plugin = (*entry)->plugin;
210 if (list)
211 {
212 *list = (*entry)->loaded;
213 }
214 return TRUE;
215 }
216
217 METHOD(plugin_loader_t, create_plugin_enumerator, enumerator_t*,
218 private_plugin_loader_t *this)
219 {
220 return enumerator_create_filter(
221 this->plugins->create_enumerator(this->plugins),
222 (void*)plugin_filter, NULL, NULL);
223 }
224
225 /**
226 * Create a list of the names of all loaded plugins
227 */
228 static char* loaded_plugins_list(private_plugin_loader_t *this)
229 {
230 int buf_len = 128, len = 0;
231 char *buf, *name;
232 enumerator_t *enumerator;
233 plugin_t *plugin;
234
235 buf = malloc(buf_len);
236 buf[0] = '\0';
237 enumerator = create_plugin_enumerator(this);
238 while (enumerator->enumerate(enumerator, &plugin, NULL))
239 {
240 name = plugin->get_name(plugin);
241 if (len + (strlen(name) + 1) >= buf_len)
242 {
243 buf_len <<= 1;
244 buf = realloc(buf, buf_len);
245 }
246 len += snprintf(&buf[len], buf_len - len, "%s ", name);
247 }
248 enumerator->destroy(enumerator);
249 if (len > 0 && buf[len - 1] == ' ')
250 {
251 buf[len - 1] = '\0';
252 }
253 return buf;
254 }
255
256
257 /**
258 * Check if a plugin is already loaded
259 */
260 static bool plugin_loaded(private_plugin_loader_t *this, char *name)
261 {
262 enumerator_t *enumerator;
263 bool found = FALSE;
264 plugin_t *plugin;
265
266 enumerator = create_plugin_enumerator(this);
267 while (enumerator->enumerate(enumerator, &plugin, NULL))
268 {
269 if (streq(plugin->get_name(plugin), name))
270 {
271 found = TRUE;
272 break;
273 }
274 }
275 enumerator->destroy(enumerator);
276 return found;
277 }
278
279 /**
280 * Check if a feature of a plugin is already loaded
281 */
282 static bool feature_loaded(private_plugin_loader_t *this, plugin_entry_t *entry,
283 plugin_feature_t *feature)
284 {
285 return entry->loaded->find_first(entry->loaded, NULL,
286 (void**)&feature) == SUCCESS;
287 }
288
289 /**
290 * Check if loading a feature of a plugin failed
291 */
292 static bool feature_failed(private_plugin_loader_t *this, plugin_entry_t *entry,
293 plugin_feature_t *feature)
294 {
295 return entry->failed->find_first(entry->failed, NULL,
296 (void**)&feature) == SUCCESS;
297 }
298
299 /**
300 * Check if dependencies are satisfied
301 */
302 static bool dependencies_satisfied(private_plugin_loader_t *this,
303 plugin_entry_t *entry, bool soft, bool report,
304 plugin_feature_t *features, int count)
305 {
306 int i;
307
308 /* first entry is provided feature, followed by dependencies */
309 for (i = 1; i < count; i++)
310 {
311 plugin_feature_t *found;
312
313 if (features[i].kind != FEATURE_DEPENDS &&
314 features[i].kind != FEATURE_SDEPEND)
315 { /* end of dependencies */
316 break;
317 }
318 found = this->loaded_features->get_match(this->loaded_features,
319 &features[i], (hashtable_equals_t)plugin_feature_matches);
320 if (!found && (features[i].kind != FEATURE_SDEPEND || soft))
321 {
322 if (report)
323 {
324 char *provide, *depend, *name;
325
326 name = entry->plugin->get_name(entry->plugin);
327 provide = plugin_feature_get_string(&features[0]);
328 depend = plugin_feature_get_string(&features[i]);
329 DBG2(DBG_LIB, "feature %s in '%s' plugin has unsatisfied "
330 "dependency: %s", provide, name, depend);
331 free(provide);
332 free(depend);
333 }
334 return FALSE;
335 }
336 }
337 return TRUE;
338 }
339
340 /**
341 * Check if a given feature is still required as dependency
342 */
343 static bool dependency_required(private_plugin_loader_t *this,
344 plugin_feature_t *dep)
345 {
346 enumerator_t *enumerator;
347 plugin_feature_t *features;
348 plugin_entry_t *entry;
349 int count, i;
350
351 enumerator = this->plugins->create_enumerator(this->plugins);
352 while (enumerator->enumerate(enumerator, &entry))
353 {
354 if (!entry->plugin->get_features)
355 { /* features not supported */
356 continue;
357 }
358 count = entry->plugin->get_features(entry->plugin, &features);
359 for (i = 0; i < count; i++)
360 {
361 if (&features[i] != dep &&
362 feature_loaded(this, entry, &features[i]))
363 {
364 while (++i < count && (features[i].kind == FEATURE_DEPENDS ||
365 features[i].kind == FEATURE_SDEPEND))
366 {
367 if (plugin_feature_matches(&features[i], dep))
368 {
369 enumerator->destroy(enumerator);
370 return TRUE;
371 }
372 }
373 }
374 }
375 }
376 enumerator->destroy(enumerator);
377 return FALSE;
378 }
379
380 /**
381 * Load plugin features in correct order
382 */
383 static int load_features(private_plugin_loader_t *this, bool soft, bool report)
384 {
385 enumerator_t *enumerator;
386 plugin_feature_t *feature, *reg;
387 plugin_entry_t *entry;
388 int count, i, loaded = 0;
389
390 enumerator = this->plugins->create_enumerator(this->plugins);
391 while (enumerator->enumerate(enumerator, &entry))
392 {
393 if (!entry->plugin->get_features)
394 { /* feature interface not supported */
395 continue;
396 }
397 reg = NULL;
398 count = entry->plugin->get_features(entry->plugin, &feature);
399 for (i = 0; i < count; i++)
400 {
401 switch (feature->kind)
402 {
403 case FEATURE_PROVIDE:
404 if (!feature_loaded(this, entry, feature) &&
405 !feature_failed(this, entry, feature) &&
406 dependencies_satisfied(this, entry, soft, report,
407 feature, count - i))
408 {
409 if (plugin_feature_load(entry->plugin, feature, reg))
410 {
411 this->loaded_features->put(this->loaded_features,
412 feature, feature);
413 entry->loaded->insert_last(entry->loaded, feature);
414 loaded++;
415 }
416 else
417 {
418 entry->failed->insert_last(entry->failed, feature);
419 }
420 }
421 break;
422 case FEATURE_REGISTER:
423 case FEATURE_CALLBACK:
424 reg = feature;
425 break;
426 default:
427 break;
428 }
429 feature++;
430 }
431 if (loaded && !report)
432 { /* got new feature, restart from beginning of list */
433 break;
434 }
435 }
436 enumerator->destroy(enumerator);
437 return loaded;
438 }
439
440 /**
441 * Try to unload plugin features on which is not depended anymore
442 */
443 static int unload_features(private_plugin_loader_t *this, plugin_entry_t *entry)
444 {
445 plugin_feature_t *feature, *reg = NULL;
446 int count, i, unloaded = 0;
447
448 count = entry->plugin->get_features(entry->plugin, &feature);
449 for (i = 0; i < count; i++)
450 {
451 switch (feature->kind)
452 {
453 case FEATURE_PROVIDE:
454 if (feature_loaded(this, entry, feature) &&
455 !dependency_required(this, feature) &&
456 plugin_feature_unload(entry->plugin, feature, reg))
457 {
458 this->loaded_features->remove(this->loaded_features,
459 feature);
460 entry->loaded->remove(entry->loaded, feature, NULL);
461 unloaded++;
462 }
463 break;
464 case FEATURE_REGISTER:
465 case FEATURE_CALLBACK:
466 reg = feature;
467 break;
468 default:
469 break;
470 }
471 feature++;
472 }
473 return unloaded;
474 }
475
476 /**
477 * Remove plugins that we were not able to load any features from.
478 */
479 static void purge_plugins(private_plugin_loader_t *this)
480 {
481 enumerator_t *enumerator;
482 plugin_entry_t *entry;
483
484 enumerator = this->plugins->create_enumerator(this->plugins);
485 while (enumerator->enumerate(enumerator, &entry))
486 {
487 if (!entry->plugin->get_features)
488 { /* feature interface not supported */
489 continue;
490 }
491 if (!entry->loaded->get_count(entry->loaded))
492 {
493 this->plugins->remove_at(this->plugins, enumerator);
494 plugin_entry_destroy(entry);
495 }
496 }
497 enumerator->destroy(enumerator);
498 }
499
500 METHOD(plugin_loader_t, load_plugins, bool,
501 private_plugin_loader_t *this, char *path, char *list)
502 {
503 enumerator_t *enumerator;
504 char *token;
505 bool critical_failed = FALSE;
506
507 if (path == NULL)
508 {
509 path = PLUGINDIR;
510 }
511
512 enumerator = enumerator_create_token(list, " ", " ");
513 while (!critical_failed && enumerator->enumerate(enumerator, &token))
514 {
515 bool critical = FALSE;
516 char file[PATH_MAX];
517 int len;
518
519 token = strdup(token);
520 len = strlen(token);
521 if (token[len-1] == '!')
522 {
523 critical = TRUE;
524 token[len-1] = '\0';
525 }
526 if (plugin_loaded(this, token))
527 {
528 free(token);
529 continue;
530 }
531 if (snprintf(file, sizeof(file), "%s/libstrongswan-%s.so",
532 path, token) >= sizeof(file))
533 {
534 return FALSE;
535 }
536 if (!load_plugin(this, token, file) && critical)
537 {
538 critical_failed = TRUE;
539 DBG1(DBG_LIB, "loading critical plugin '%s' failed", token);
540 }
541 free(token);
542 /* TODO: we currently load features after each plugin is loaded. This
543 * will not be necessary once we have features support in all plugins.
544 */
545 while (load_features(this, TRUE, FALSE))
546 {
547 /* try load new features until we don't get new ones */
548 }
549 }
550 enumerator->destroy(enumerator);
551 if (!critical_failed)
552 {
553 while (load_features(this, FALSE, FALSE))
554 {
555 /* enforce loading features, ignoring soft dependencies */
556 }
557 /* report missing dependencies */
558 load_features(this, FALSE, TRUE);
559 /* unload plugins that we were not able to load any features for */
560 purge_plugins(this);
561 }
562 if (!critical_failed)
563 {
564 free(this->loaded_plugins);
565 this->loaded_plugins = loaded_plugins_list(this);
566 }
567 return !critical_failed;
568 }
569
570 METHOD(plugin_loader_t, unload, void,
571 private_plugin_loader_t *this)
572 {
573 enumerator_t *enumerator;
574 plugin_entry_t *entry;
575 linked_list_t *list;
576
577 /* unload plugins in reverse order, for those not supporting features */
578 list = linked_list_create();
579 while (this->plugins->remove_last(this->plugins, (void**)&entry) == SUCCESS)
580 {
581 list->insert_last(list, entry);
582 }
583 while (list->remove_last(list, (void**)&entry) == SUCCESS)
584 {
585 this->plugins->insert_first(this->plugins, entry);
586 }
587 list->destroy(list);
588 while (this->plugins->get_count(this->plugins))
589 {
590 enumerator = this->plugins->create_enumerator(this->plugins);
591 while (enumerator->enumerate(enumerator, &entry))
592 {
593 if (entry->plugin->get_features)
594 { /* supports features */
595 while (unload_features(this, entry));
596 }
597 if (entry->loaded->get_count(entry->loaded) == 0)
598 {
599 if (lib->leak_detective)
600 { /* keep handle to report leaks properly */
601 entry->handle = NULL;
602 }
603 this->plugins->remove_at(this->plugins, enumerator);
604 plugin_entry_destroy(entry);
605 }
606 }
607 enumerator->destroy(enumerator);
608 }
609 free(this->loaded_plugins);
610 this->loaded_plugins = NULL;
611 }
612
613 /**
614 * Reload a plugin by name, NULL for all
615 */
616 static u_int reload_by_name(private_plugin_loader_t *this, char *name)
617 {
618 u_int reloaded = 0;
619 enumerator_t *enumerator;
620 plugin_t *plugin;
621
622 enumerator = create_plugin_enumerator(this);
623 while (enumerator->enumerate(enumerator, &plugin, NULL))
624 {
625 if (name == NULL || streq(name, plugin->get_name(plugin)))
626 {
627 if (plugin->reload && plugin->reload(plugin))
628 {
629 DBG2(DBG_LIB, "reloaded configuration of '%s' plugin",
630 plugin->get_name(plugin));
631 reloaded++;
632 }
633 }
634 }
635 enumerator->destroy(enumerator);
636 return reloaded;
637 }
638
639 METHOD(plugin_loader_t, reload, u_int,
640 private_plugin_loader_t *this, char *list)
641 {
642 u_int reloaded = 0;
643 enumerator_t *enumerator;
644 char *name;
645
646 if (list == NULL)
647 {
648 return reload_by_name(this, NULL);
649 }
650 enumerator = enumerator_create_token(list, " ", "");
651 while (enumerator->enumerate(enumerator, &name))
652 {
653 reloaded += reload_by_name(this, name);
654 }
655 enumerator->destroy(enumerator);
656 return reloaded;
657 }
658
659 METHOD(plugin_loader_t, loaded_plugins, char*,
660 private_plugin_loader_t *this)
661 {
662 return this->loaded_plugins ?: "";
663 }
664
665 METHOD(plugin_loader_t, destroy, void,
666 private_plugin_loader_t *this)
667 {
668 unload(this);
669 this->loaded_features->destroy(this->loaded_features);
670 this->plugins->destroy(this->plugins);
671 free(this->loaded_plugins);
672 free(this);
673 }
674
675 /*
676 * see header file
677 */
678 plugin_loader_t *plugin_loader_create()
679 {
680 private_plugin_loader_t *this;
681
682 INIT(this,
683 .public = {
684 .load = _load_plugins,
685 .reload = _reload,
686 .unload = _unload,
687 .create_plugin_enumerator = _create_plugin_enumerator,
688 .loaded_plugins = _loaded_plugins,
689 .destroy = _destroy,
690 },
691 .plugins = linked_list_create(),
692 .loaded_features = hashtable_create(
693 (hashtable_hash_t)plugin_feature_hash,
694 (hashtable_equals_t)plugin_feature_equals, 64),
695 );
696
697 return &this->public;
698 }
699