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