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