Merge branch 'ikev1-clean' into ikev1-master
[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 (feature_loaded(this, entry, &features[i]))
365 {
366 while (++i < count && (features[i].kind == FEATURE_DEPENDS ||
367 features[i].kind == FEATURE_SDEPEND))
368 {
369 if (plugin_feature_matches(&features[i], dep))
370 {
371 enumerator->destroy(enumerator);
372 return TRUE;
373 }
374 }
375 }
376 }
377 }
378 enumerator->destroy(enumerator);
379 return FALSE;
380 }
381
382 /**
383 * Load plugin features in correct order
384 */
385 static int load_features(private_plugin_loader_t *this, bool soft, bool report)
386 {
387 enumerator_t *enumerator;
388 plugin_feature_t *feature, *reg;
389 plugin_entry_t *entry;
390 int count, i, loaded = 0;
391
392 enumerator = this->plugins->create_enumerator(this->plugins);
393 while (enumerator->enumerate(enumerator, &entry))
394 {
395 if (!entry->plugin->get_features)
396 { /* feature interface not supported */
397 continue;
398 }
399 reg = NULL;
400 count = entry->plugin->get_features(entry->plugin, &feature);
401 for (i = 0; i < count; i++)
402 {
403 switch (feature->kind)
404 {
405 case FEATURE_PROVIDE:
406 if (!feature_loaded(this, entry, feature) &&
407 !feature_failed(this, entry, feature) &&
408 dependencies_satisfied(this, entry, soft, report,
409 feature, count - i))
410 {
411 if (plugin_feature_load(entry->plugin, feature, reg))
412 {
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 }
432 enumerator->destroy(enumerator);
433 return loaded;
434 }
435
436 /**
437 * Try to unload plugin features on which is not depended anymore
438 */
439 static int unload_features(private_plugin_loader_t *this, plugin_entry_t *entry)
440 {
441 plugin_feature_t *feature, *reg = NULL;
442 int count, i, unloaded = 0;
443
444 count = entry->plugin->get_features(entry->plugin, &feature);
445 for (i = 0; i < count; i++)
446 {
447 switch (feature->kind)
448 {
449 case FEATURE_PROVIDE:
450 if (feature_loaded(this, entry, feature) &&
451 !dependency_required(this, feature) &&
452 plugin_feature_unload(entry->plugin, feature, reg))
453 {
454 entry->loaded->remove(entry->loaded, feature, NULL);
455 unloaded++;
456 }
457 break;
458 case FEATURE_REGISTER:
459 case FEATURE_CALLBACK:
460 reg = feature;
461 break;
462 default:
463 break;
464 }
465 feature++;
466 }
467 return unloaded;
468 }
469
470 /**
471 * Remove plugins that we were not able to load any features from.
472 */
473 static void purge_plugins(private_plugin_loader_t *this)
474 {
475 enumerator_t *enumerator;
476 plugin_entry_t *entry;
477
478 enumerator = this->plugins->create_enumerator(this->plugins);
479 while (enumerator->enumerate(enumerator, &entry))
480 {
481 if (!entry->plugin->get_features)
482 { /* feature interface not supported */
483 continue;
484 }
485 if (!entry->loaded->get_count(entry->loaded))
486 {
487 this->plugins->remove_at(this->plugins, enumerator);
488 plugin_entry_destroy(entry);
489 }
490 }
491 enumerator->destroy(enumerator);
492 }
493
494 METHOD(plugin_loader_t, load_plugins, bool,
495 private_plugin_loader_t *this, char *path, char *list)
496 {
497 enumerator_t *enumerator;
498 char *token;
499 bool critical_failed = FALSE;
500
501 if (path == NULL)
502 {
503 path = PLUGINDIR;
504 }
505
506 enumerator = enumerator_create_token(list, " ", " ");
507 while (!critical_failed && enumerator->enumerate(enumerator, &token))
508 {
509 bool critical = FALSE;
510 char file[PATH_MAX];
511 int len;
512
513 token = strdup(token);
514 len = strlen(token);
515 if (token[len-1] == '!')
516 {
517 critical = TRUE;
518 token[len-1] = '\0';
519 }
520 if (plugin_loaded(this, token))
521 {
522 free(token);
523 continue;
524 }
525 if (snprintf(file, sizeof(file), "%s/libstrongswan-%s.so",
526 path, token) >= sizeof(file))
527 {
528 return NULL;
529 }
530 if (!load_plugin(this, token, file) && critical)
531 {
532 critical_failed = TRUE;
533 DBG1(DBG_LIB, "loading critical plugin '%s' failed", token);
534 }
535 free(token);
536 /* TODO: we currently load features after each plugin is loaded. This
537 * will not be necessary once we have features support in all plugins.
538 */
539 while (load_features(this, TRUE, FALSE))
540 {
541 /* try load new features until we don't get new ones */
542 }
543 }
544 enumerator->destroy(enumerator);
545 if (!critical_failed)
546 {
547 while (load_features(this, FALSE, FALSE))
548 {
549 /* enforce loading features, ignoring soft dependencies */
550 }
551 /* report missing dependencies */
552 load_features(this, FALSE, TRUE);
553 /* unload plugins that we were not able to load any features for */
554 purge_plugins(this);
555 }
556 if (!critical_failed)
557 {
558 free(this->loaded_plugins);
559 this->loaded_plugins = loaded_plugins_list(this);
560 }
561 return !critical_failed;
562 }
563
564 METHOD(plugin_loader_t, unload, void,
565 private_plugin_loader_t *this)
566 {
567 enumerator_t *enumerator;
568 plugin_entry_t *entry;
569 linked_list_t *list;
570
571 /* unload plugins in reverse order, for those not supporting features */
572 list = linked_list_create();
573 while (this->plugins->remove_last(this->plugins, (void**)&entry) == SUCCESS)
574 {
575 list->insert_last(list, entry);
576 }
577 while (list->remove_last(list, (void**)&entry) == SUCCESS)
578 {
579 this->plugins->insert_first(this->plugins, entry);
580 }
581 list->destroy(list);
582 while (this->plugins->get_count(this->plugins))
583 {
584 enumerator = this->plugins->create_enumerator(this->plugins);
585 while (enumerator->enumerate(enumerator, &entry))
586 {
587 if (entry->plugin->get_features)
588 { /* supports features */
589 while (unload_features(this, entry));
590 }
591 if (entry->loaded->get_count(entry->loaded) == 0)
592 {
593 if (lib->leak_detective)
594 { /* keep handle to report leaks properly */
595 entry->handle = NULL;
596 }
597 this->plugins->remove_at(this->plugins, enumerator);
598 plugin_entry_destroy(entry);
599 }
600 }
601 enumerator->destroy(enumerator);
602 }
603 free(this->loaded_plugins);
604 this->loaded_plugins = NULL;
605 }
606
607 /**
608 * Reload a plugin by name, NULL for all
609 */
610 static u_int reload_by_name(private_plugin_loader_t *this, char *name)
611 {
612 u_int reloaded = 0;
613 enumerator_t *enumerator;
614 plugin_t *plugin;
615
616 enumerator = create_plugin_enumerator(this);
617 while (enumerator->enumerate(enumerator, &plugin, NULL))
618 {
619 if (name == NULL || streq(name, plugin->get_name(plugin)))
620 {
621 if (plugin->reload && plugin->reload(plugin))
622 {
623 DBG2(DBG_LIB, "reloaded configuration of '%s' plugin",
624 plugin->get_name(plugin));
625 reloaded++;
626 }
627 }
628 }
629 enumerator->destroy(enumerator);
630 return reloaded;
631 }
632
633 METHOD(plugin_loader_t, reload, u_int,
634 private_plugin_loader_t *this, char *list)
635 {
636 u_int reloaded = 0;
637 enumerator_t *enumerator;
638 char *name;
639
640 if (list == NULL)
641 {
642 return reload_by_name(this, NULL);
643 }
644 enumerator = enumerator_create_token(list, " ", "");
645 while (enumerator->enumerate(enumerator, &name))
646 {
647 reloaded += reload_by_name(this, name);
648 }
649 enumerator->destroy(enumerator);
650 return reloaded;
651 }
652
653 METHOD(plugin_loader_t, loaded_plugins, char*,
654 private_plugin_loader_t *this)
655 {
656 return this->loaded_plugins ?: "";
657 }
658
659 METHOD(plugin_loader_t, destroy, void,
660 private_plugin_loader_t *this)
661 {
662 unload(this);
663 this->plugins->destroy(this->plugins);
664 free(this->loaded_plugins);
665 free(this);
666 }
667
668 /*
669 * see header file
670 */
671 plugin_loader_t *plugin_loader_create()
672 {
673 private_plugin_loader_t *this;
674
675 INIT(this,
676 .public = {
677 .load = _load_plugins,
678 .reload = _reload,
679 .unload = _unload,
680 .create_plugin_enumerator = _create_plugin_enumerator,
681 .loaded_plugins = _loaded_plugins,
682 .destroy = _destroy,
683 },
684 .plugins = linked_list_create(),
685 );
686
687 return &this->public;
688 }
689