]>
Commit | Line | Data |
---|---|---|
552cc11b | 1 | /* |
190a2788 | 2 | * Copyright (C) 2010-2014 Tobias Brunner |
552cc11b | 3 | * Copyright (C) 2007 Martin Willi |
19ef2aec TB |
4 | * |
5 | * Copyright (C) secunet Security Networks AG | |
552cc11b MW |
6 | * |
7 | * This program is free software; you can redistribute it and/or modify it | |
8 | * under the terms of the GNU General Public License as published by the | |
9 | * Free Software Foundation; either version 2 of the License, or (at your | |
10 | * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. | |
11 | * | |
12 | * This program is distributed in the hope that it will be useful, but | |
13 | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY | |
14 | * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
15 | * for more details. | |
552cc11b MW |
16 | */ |
17 | ||
eec675bf | 18 | #define _GNU_SOURCE |
552cc11b MW |
19 | #include "plugin_loader.h" |
20 | ||
f2086e42 TB |
21 | #include <sys/types.h> |
22 | #include <sys/stat.h> | |
23 | #include <unistd.h> | |
a3d92a37 | 24 | #include <string.h> |
2e6c203b | 25 | #ifdef HAVE_DLADDR |
552cc11b | 26 | #include <dlfcn.h> |
2e6c203b | 27 | #endif |
a3d92a37 MW |
28 | #include <limits.h> |
29 | #include <stdio.h> | |
552cc11b | 30 | |
f05b4272 | 31 | #include <utils/debug.h> |
629fd2f4 | 32 | #include <library.h> |
12642a68 | 33 | #include <collections/hashtable.h> |
190a2788 | 34 | #include <collections/array.h> |
12642a68 | 35 | #include <collections/linked_list.h> |
552cc11b | 36 | #include <plugins/plugin.h> |
08944b68 | 37 | #include <utils/integrity_checker.h> |
552cc11b MW |
38 | |
39 | typedef struct private_plugin_loader_t private_plugin_loader_t; | |
49d7a98f TB |
40 | typedef struct registered_feature_t registered_feature_t; |
41 | typedef struct provided_feature_t provided_feature_t; | |
62b9e2f9 | 42 | typedef struct plugin_entry_t plugin_entry_t; |
552cc11b | 43 | |
1a06bf03 TB |
44 | #ifdef STATIC_PLUGIN_CONSTRUCTORS |
45 | /** | |
46 | * Statically registered constructors | |
47 | */ | |
48 | static hashtable_t *plugin_constructors = NULL; | |
49 | #endif | |
50 | ||
552cc11b MW |
51 | /** |
52 | * private data of plugin_loader | |
53 | */ | |
54 | struct private_plugin_loader_t { | |
55 | ||
56 | /** | |
57 | * public functions | |
58 | */ | |
59 | plugin_loader_t public; | |
7daf5226 | 60 | |
552cc11b | 61 | /** |
62b9e2f9 | 62 | * List of plugins, as plugin_entry_t |
552cc11b MW |
63 | */ |
64 | linked_list_t *plugins; | |
f1ba06c1 | 65 | |
9eac6106 | 66 | /** |
49d7a98f | 67 | * Hashtable for registered features, as registered_feature_t |
9eac6106 | 68 | */ |
d9944102 | 69 | hashlist_t *features; |
49d7a98f TB |
70 | |
71 | /** | |
72 | * Loaded features (stored in reverse order), as provided_feature_t | |
73 | */ | |
74 | linked_list_t *loaded; | |
9eac6106 | 75 | |
f2086e42 TB |
76 | /** |
77 | * List of paths to search for plugins | |
78 | */ | |
79 | linked_list_t *paths; | |
80 | ||
f1ba06c1 TB |
81 | /** |
82 | * List of names of loaded plugins | |
83 | */ | |
84 | char *loaded_plugins; | |
34ee14dd TB |
85 | |
86 | /** | |
87 | * Statistics collected while loading features | |
88 | */ | |
89 | struct { | |
90 | /** Number of features that failed to load */ | |
91 | int failed; | |
92 | /** Number of features that failed because of unmet dependencies */ | |
93 | int depends; | |
94 | /** Number of features in critical plugins that failed to load */ | |
95 | int critical; | |
96 | } stats; | |
2566eb21 TE |
97 | |
98 | /** | |
99 | * Fetch features from the given plugin, can optionally be overridden to | |
100 | * modify feature arrays at loading time | |
101 | */ | |
102 | int (*get_features)(plugin_t *plugin, plugin_feature_t *features[]); | |
552cc11b MW |
103 | }; |
104 | ||
49d7a98f TB |
105 | /** |
106 | * Registered plugin feature | |
107 | */ | |
108 | struct registered_feature_t { | |
109 | ||
110 | /** | |
111 | * The registered feature | |
112 | */ | |
113 | plugin_feature_t *feature; | |
114 | ||
115 | /** | |
116 | * List of plugins providing this feature, as provided_feature_t | |
117 | */ | |
118 | linked_list_t *plugins; | |
119 | }; | |
120 | ||
121 | /** | |
122 | * Hash a registered feature | |
123 | */ | |
ac4942c3 | 124 | static u_int registered_feature_hash(registered_feature_t *this) |
49d7a98f TB |
125 | { |
126 | return plugin_feature_hash(this->feature); | |
127 | } | |
128 | ||
129 | /** | |
130 | * Compare two registered features | |
131 | */ | |
132 | static bool registered_feature_equals(registered_feature_t *a, | |
133 | registered_feature_t *b) | |
134 | { | |
135 | return plugin_feature_equals(a->feature, b->feature); | |
136 | } | |
137 | ||
138 | /** | |
139 | * Feature as provided by a plugin | |
140 | */ | |
141 | struct provided_feature_t { | |
142 | ||
143 | /** | |
144 | * Plugin providing the feature | |
145 | */ | |
146 | plugin_entry_t *entry; | |
147 | ||
148 | /** | |
149 | * FEATURE_REGISTER or FEATURE_CALLBACK entry | |
150 | */ | |
151 | plugin_feature_t *reg; | |
152 | ||
153 | /** | |
154 | * The provided feature (followed by dependencies) | |
155 | */ | |
156 | plugin_feature_t *feature; | |
157 | ||
158 | /** | |
159 | * Maximum number of dependencies (following feature) | |
160 | */ | |
161 | int dependencies; | |
162 | ||
163 | /** | |
164 | * TRUE if currently loading this feature (to prevent loops) | |
165 | */ | |
166 | bool loading; | |
167 | ||
168 | /** | |
169 | * TRUE if feature loaded | |
170 | */ | |
171 | bool loaded; | |
172 | ||
173 | /** | |
174 | * TRUE if feature failed to load | |
175 | */ | |
176 | bool failed; | |
177 | }; | |
178 | ||
62b9e2f9 MW |
179 | /** |
180 | * Entry for a plugin | |
181 | */ | |
182 | struct plugin_entry_t { | |
183 | ||
184 | /** | |
185 | * Plugin instance | |
186 | */ | |
187 | plugin_t *plugin; | |
188 | ||
e0712243 TB |
189 | /** |
190 | * TRUE, if the plugin is marked as critical | |
191 | */ | |
192 | bool critical; | |
193 | ||
62b9e2f9 MW |
194 | /** |
195 | * dlopen handle, if in separate lib | |
196 | */ | |
197 | void *handle; | |
198 | ||
199 | /** | |
49d7a98f | 200 | * List of features, as provided_feature_t |
a07f2a4b | 201 | */ |
49d7a98f | 202 | linked_list_t *features; |
62b9e2f9 MW |
203 | }; |
204 | ||
205 | /** | |
206 | * Destroy a plugin entry | |
207 | */ | |
208 | static void plugin_entry_destroy(plugin_entry_t *entry) | |
209 | { | |
210 | DESTROY_IF(entry->plugin); | |
211 | if (entry->handle) | |
212 | { | |
213 | dlclose(entry->handle); | |
214 | } | |
49d7a98f | 215 | entry->features->destroy(entry->features); |
62b9e2f9 MW |
216 | free(entry); |
217 | } | |
218 | ||
18d21a57 TB |
219 | /** |
220 | * Wrapper for static plugin features | |
221 | */ | |
222 | typedef struct { | |
223 | ||
224 | /** | |
225 | * Implements plugin_t interface | |
226 | */ | |
227 | plugin_t public; | |
228 | ||
229 | /** | |
230 | * Name of the module registering these features | |
231 | */ | |
232 | char *name; | |
233 | ||
5421092b MW |
234 | /** |
235 | * Optional reload function for features | |
236 | */ | |
237 | bool (*reload)(void *data); | |
238 | ||
239 | /** | |
240 | * User data to pass to reload function | |
241 | */ | |
242 | void *reload_data; | |
243 | ||
18d21a57 TB |
244 | /** |
245 | * Static plugin features | |
246 | */ | |
247 | plugin_feature_t *features; | |
248 | ||
249 | /** | |
250 | * Number of plugin features | |
251 | */ | |
252 | int count; | |
253 | ||
254 | } static_features_t; | |
255 | ||
256 | METHOD(plugin_t, get_static_name, char*, | |
257 | static_features_t *this) | |
258 | { | |
259 | return this->name; | |
260 | } | |
261 | ||
262 | METHOD(plugin_t, get_static_features, int, | |
263 | static_features_t *this, plugin_feature_t *features[]) | |
264 | { | |
265 | *features = this->features; | |
266 | return this->count; | |
267 | } | |
268 | ||
5421092b MW |
269 | METHOD(plugin_t, static_reload, bool, |
270 | static_features_t *this) | |
271 | { | |
272 | if (this->reload) | |
273 | { | |
274 | return this->reload(this->reload_data); | |
275 | } | |
276 | return FALSE; | |
277 | } | |
278 | ||
18d21a57 TB |
279 | METHOD(plugin_t, static_destroy, void, |
280 | static_features_t *this) | |
281 | { | |
282 | free(this->features); | |
283 | free(this->name); | |
284 | free(this); | |
285 | } | |
286 | ||
287 | /** | |
288 | * Create a wrapper around static plugin features. | |
289 | */ | |
290 | static plugin_t *static_features_create(const char *name, | |
5421092b MW |
291 | plugin_feature_t features[], int count, |
292 | bool (*reload)(void*), void *reload_data) | |
18d21a57 TB |
293 | { |
294 | static_features_t *this; | |
295 | ||
296 | INIT(this, | |
297 | .public = { | |
298 | .get_name = _get_static_name, | |
299 | .get_features = _get_static_features, | |
5421092b | 300 | .reload = _static_reload, |
18d21a57 TB |
301 | .destroy = _static_destroy, |
302 | }, | |
303 | .name = strdup(name), | |
5421092b MW |
304 | .reload = reload, |
305 | .reload_data = reload_data, | |
18d21a57 TB |
306 | .features = calloc(count, sizeof(plugin_feature_t)), |
307 | .count = count, | |
308 | ); | |
309 | ||
310 | memcpy(this->features, features, sizeof(plugin_feature_t) * count); | |
311 | ||
312 | return &this->public; | |
313 | } | |
314 | ||
1a06bf03 TB |
315 | #ifdef STATIC_PLUGIN_CONSTRUCTORS |
316 | /* | |
317 | * Described in header. | |
318 | */ | |
319 | void plugin_constructor_register(char *name, void *constructor) | |
320 | { | |
321 | bool old = FALSE; | |
322 | ||
323 | if (lib && lib->leak_detective) | |
324 | { | |
325 | old = lib->leak_detective->set_state(lib->leak_detective, FALSE); | |
326 | } | |
327 | ||
328 | if (!plugin_constructors) | |
329 | { | |
330 | chunk_hash_seed(); | |
331 | plugin_constructors = hashtable_create(hashtable_hash_str, | |
332 | hashtable_equals_str, 32); | |
333 | } | |
334 | if (constructor) | |
335 | { | |
336 | plugin_constructors->put(plugin_constructors, name, constructor); | |
337 | } | |
338 | else | |
339 | { | |
340 | plugin_constructors->remove(plugin_constructors, name); | |
341 | if (!plugin_constructors->get_count(plugin_constructors)) | |
342 | { | |
343 | plugin_constructors->destroy(plugin_constructors); | |
344 | plugin_constructors = NULL; | |
345 | } | |
346 | } | |
347 | ||
348 | if (lib && lib->leak_detective) | |
349 | { | |
350 | lib->leak_detective->set_state(lib->leak_detective, old); | |
351 | } | |
352 | } | |
353 | #endif | |
354 | ||
3724668b | 355 | /** |
6302f497 TB |
356 | * create a plugin |
357 | * returns: NOT_FOUND, if the constructor was not found | |
358 | * FAILED, if the plugin could not be constructed | |
3724668b | 359 | */ |
6302f497 | 360 | static status_t create_plugin(private_plugin_loader_t *this, void *handle, |
af9c78d3 TB |
361 | char *name, char *create, bool integrity, |
362 | bool critical, plugin_entry_t **entry) | |
3724668b | 363 | { |
62b9e2f9 | 364 | plugin_t *plugin; |
1a06bf03 | 365 | plugin_constructor_t constructor = NULL; |
3724668b | 366 | |
1a06bf03 TB |
367 | #ifdef STATIC_PLUGIN_CONSTRUCTORS |
368 | if (plugin_constructors) | |
369 | { | |
370 | constructor = plugin_constructors->get(plugin_constructors, name); | |
371 | } | |
372 | if (!constructor) | |
373 | #endif | |
374 | { | |
375 | constructor = dlsym(handle, create); | |
376 | } | |
377 | if (!constructor) | |
3724668b | 378 | { |
6302f497 TB |
379 | return NOT_FOUND; |
380 | } | |
381 | if (integrity && lib->integrity) | |
382 | { | |
383 | if (!lib->integrity->check_segment(lib->integrity, name, constructor)) | |
384 | { | |
385 | DBG1(DBG_LIB, "plugin '%s': failed segment integrity test", name); | |
386 | return FAILED; | |
387 | } | |
388 | DBG1(DBG_LIB, "plugin '%s': passed file and segment integrity tests", | |
389 | name); | |
3724668b | 390 | } |
62b9e2f9 MW |
391 | plugin = constructor(); |
392 | if (plugin == NULL) | |
3724668b | 393 | { |
8b0e0910 TB |
394 | DBG1(DBG_LIB, "plugin '%s': failed to load - %s returned NULL", name, |
395 | create); | |
6302f497 | 396 | return FAILED; |
3724668b | 397 | } |
62b9e2f9 MW |
398 | INIT(*entry, |
399 | .plugin = plugin, | |
e0712243 | 400 | .critical = critical, |
49d7a98f | 401 | .features = linked_list_create(), |
62b9e2f9 | 402 | ); |
8b0e0910 | 403 | DBG2(DBG_LIB, "plugin '%s': loaded successfully", name); |
6302f497 | 404 | return SUCCESS; |
3724668b | 405 | } |
6302f497 | 406 | |
552cc11b | 407 | /** |
a3d92a37 | 408 | * load a single plugin |
552cc11b | 409 | */ |
49d7a98f TB |
410 | static plugin_entry_t *load_plugin(private_plugin_loader_t *this, char *name, |
411 | char *file, bool critical) | |
552cc11b | 412 | { |
af9c78d3 | 413 | char create[128]; |
62b9e2f9 | 414 | plugin_entry_t *entry; |
552cc11b | 415 | void *handle; |
305c4aa8 | 416 | int flag = RTLD_LAZY; |
6302f497 | 417 | |
af9c78d3 TB |
418 | if (snprintf(create, sizeof(create), "%s_plugin_create", |
419 | name) >= sizeof(create)) | |
420 | { | |
421 | return NULL; | |
422 | } | |
423 | translate(create, "-", "_"); | |
424 | switch (create_plugin(this, RTLD_DEFAULT, name, create, FALSE, critical, | |
425 | &entry)) | |
6302f497 TB |
426 | { |
427 | case SUCCESS: | |
62b9e2f9 | 428 | this->plugins->insert_last(this->plugins, entry); |
49d7a98f | 429 | return entry; |
6302f497 | 430 | case NOT_FOUND: |
a9f169f6 TB |
431 | if (file) |
432 | { /* try to load the plugin from a file */ | |
433 | break; | |
434 | } | |
af9c78d3 TB |
435 | DBG1(DBG_LIB, "plugin '%s': failed to load - %s not found and no " |
436 | "plugin file available", name, create); | |
a9f169f6 | 437 | /* fall-through */ |
6302f497 | 438 | default: |
49d7a98f | 439 | return NULL; |
9ce567f8 | 440 | } |
6b04ba28 | 441 | if (lib->integrity) |
960e0c10 | 442 | { |
6b04ba28 AS |
443 | if (!lib->integrity->check_file(lib->integrity, name, file)) |
444 | { | |
8b0e0910 TB |
445 | DBG1(DBG_LIB, "plugin '%s': failed file integrity test of '%s'", |
446 | name, file); | |
49d7a98f | 447 | return NULL; |
6b04ba28 | 448 | } |
960e0c10 | 449 | } |
305c4aa8 | 450 | if (lib->settings->get_bool(lib->settings, "%s.dlopen_use_rtld_now", |
ddfb5dd4 | 451 | FALSE, lib->ns)) |
305c4aa8 TB |
452 | { |
453 | flag = RTLD_NOW; | |
454 | } | |
20a0fd92 | 455 | #ifdef RTLD_NODELETE |
305c4aa8 TB |
456 | /* If supported, do not unload the library when unloading a plugin. It |
457 | * really doesn't matter in productive systems, but causes many (dependency) | |
458 | * library reloads during unit tests. Some libraries can't handle that, e.g. | |
20a0fd92 | 459 | * GnuTLS leaks file descriptors in its library load/unload functions. */ |
305c4aa8 | 460 | flag |= RTLD_NODELETE; |
20a0fd92 | 461 | #endif |
305c4aa8 | 462 | handle = dlopen(file, flag); |
a3d92a37 | 463 | if (handle == NULL) |
552cc11b | 464 | { |
c60b69e4 | 465 | DBG1(DBG_LIB, "plugin '%s' failed to load: %s", name, dlerror()); |
49d7a98f | 466 | return NULL; |
552cc11b | 467 | } |
af9c78d3 | 468 | switch (create_plugin(this, handle, name, create, TRUE, critical, &entry)) |
a3d92a37 | 469 | { |
af9c78d3 TB |
470 | case SUCCESS: |
471 | break; | |
472 | case NOT_FOUND: | |
473 | DBG1(DBG_LIB, "plugin '%s': failed to load - %s not found", name, | |
474 | create); | |
475 | /* fall-through */ | |
476 | default: | |
477 | dlclose(handle); | |
478 | return NULL; | |
a3d92a37 | 479 | } |
62b9e2f9 MW |
480 | entry->handle = handle; |
481 | this->plugins->insert_last(this->plugins, entry); | |
49d7a98f TB |
482 | return entry; |
483 | } | |
484 | ||
525cc46c TB |
485 | CALLBACK(feature_filter, bool, |
486 | void *null, enumerator_t *orig, va_list args) | |
49d7a98f | 487 | { |
525cc46c TB |
488 | provided_feature_t *provided; |
489 | plugin_feature_t **feature; | |
490 | ||
491 | VA_ARGS_VGET(args, feature); | |
492 | ||
493 | while (orig->enumerate(orig, &provided)) | |
494 | { | |
495 | if (provided->loaded) | |
496 | { | |
497 | *feature = provided->feature; | |
498 | return TRUE; | |
499 | } | |
500 | } | |
501 | return FALSE; | |
62b9e2f9 MW |
502 | } |
503 | ||
525cc46c TB |
504 | CALLBACK(plugin_filter, bool, |
505 | void *null, enumerator_t *orig, va_list args) | |
62b9e2f9 | 506 | { |
525cc46c TB |
507 | plugin_entry_t *entry; |
508 | linked_list_t **list; | |
509 | plugin_t **plugin; | |
49d7a98f | 510 | |
525cc46c TB |
511 | VA_ARGS_VGET(args, plugin, list); |
512 | ||
513 | if (orig->enumerate(orig, &entry)) | |
fa7c8338 | 514 | { |
525cc46c TB |
515 | *plugin = entry->plugin; |
516 | if (list) | |
517 | { | |
518 | enumerator_t *features; | |
519 | features = enumerator_create_filter( | |
520 | entry->features->create_enumerator(entry->features), | |
521 | feature_filter, NULL, NULL); | |
522 | *list = linked_list_create_from_enumerator(features); | |
523 | } | |
524 | return TRUE; | |
fa7c8338 | 525 | } |
525cc46c | 526 | return FALSE; |
62b9e2f9 MW |
527 | } |
528 | ||
529 | METHOD(plugin_loader_t, create_plugin_enumerator, enumerator_t*, | |
530 | private_plugin_loader_t *this) | |
531 | { | |
532 | return enumerator_create_filter( | |
533 | this->plugins->create_enumerator(this->plugins), | |
525cc46c | 534 | plugin_filter, NULL, NULL); |
a3d92a37 MW |
535 | } |
536 | ||
2bedb0f2 MW |
537 | METHOD(plugin_loader_t, has_feature, bool, |
538 | private_plugin_loader_t *this, plugin_feature_t feature) | |
539 | { | |
540 | enumerator_t *plugins, *features; | |
541 | plugin_t *plugin; | |
542 | linked_list_t *list; | |
543 | plugin_feature_t *current; | |
544 | bool found = FALSE; | |
545 | ||
546 | plugins = create_plugin_enumerator(this); | |
547 | while (plugins->enumerate(plugins, &plugin, &list)) | |
548 | { | |
549 | features = list->create_enumerator(list); | |
550 | while (features->enumerate(features, ¤t)) | |
551 | { | |
552 | if (plugin_feature_matches(&feature, current)) | |
553 | { | |
554 | found = TRUE; | |
555 | break; | |
556 | } | |
557 | } | |
558 | features->destroy(features); | |
559 | list->destroy(list); | |
560 | } | |
561 | plugins->destroy(plugins); | |
562 | ||
563 | return found; | |
564 | } | |
565 | ||
f1ba06c1 TB |
566 | /** |
567 | * Create a list of the names of all loaded plugins | |
568 | */ | |
569 | static char* loaded_plugins_list(private_plugin_loader_t *this) | |
570 | { | |
571 | int buf_len = 128, len = 0; | |
572 | char *buf, *name; | |
573 | enumerator_t *enumerator; | |
574 | plugin_t *plugin; | |
575 | ||
576 | buf = malloc(buf_len); | |
577 | buf[0] = '\0'; | |
578 | enumerator = create_plugin_enumerator(this); | |
579 | while (enumerator->enumerate(enumerator, &plugin, NULL)) | |
580 | { | |
581 | name = plugin->get_name(plugin); | |
582 | if (len + (strlen(name) + 1) >= buf_len) | |
583 | { | |
584 | buf_len <<= 1; | |
585 | buf = realloc(buf, buf_len); | |
586 | } | |
587 | len += snprintf(&buf[len], buf_len - len, "%s ", name); | |
588 | } | |
589 | enumerator->destroy(enumerator); | |
590 | if (len > 0 && buf[len - 1] == ' ') | |
591 | { | |
592 | buf[len - 1] = '\0'; | |
593 | } | |
594 | return buf; | |
595 | } | |
596 | ||
1535913a MW |
597 | /** |
598 | * Check if a plugin is already loaded | |
599 | */ | |
600 | static bool plugin_loaded(private_plugin_loader_t *this, char *name) | |
601 | { | |
602 | enumerator_t *enumerator; | |
603 | bool found = FALSE; | |
787b5884 | 604 | plugin_t *plugin; |
1535913a | 605 | |
62b9e2f9 MW |
606 | enumerator = create_plugin_enumerator(this); |
607 | while (enumerator->enumerate(enumerator, &plugin, NULL)) | |
1535913a | 608 | { |
787b5884 | 609 | if (streq(plugin->get_name(plugin), name)) |
1535913a MW |
610 | { |
611 | found = TRUE; | |
612 | break; | |
613 | } | |
614 | } | |
615 | enumerator->destroy(enumerator); | |
616 | return found; | |
617 | } | |
618 | ||
62b9e2f9 | 619 | /** |
49d7a98f TB |
620 | * Forward declaration |
621 | */ | |
622 | static void load_provided(private_plugin_loader_t *this, | |
623 | provided_feature_t *provided, | |
624 | int level); | |
625 | ||
2e4d110d TB |
626 | CALLBACK(is_feature_loaded, bool, |
627 | provided_feature_t *item, va_list args) | |
49d7a98f TB |
628 | { |
629 | return item->loaded; | |
630 | } | |
631 | ||
2e4d110d TB |
632 | CALLBACK(is_feature_loadable, bool, |
633 | provided_feature_t *item, va_list args) | |
62b9e2f9 | 634 | { |
49d7a98f | 635 | return !item->loading && !item->loaded && !item->failed; |
62b9e2f9 MW |
636 | } |
637 | ||
a07f2a4b | 638 | /** |
49d7a98f | 639 | * Find a loaded and matching feature |
a07f2a4b | 640 | */ |
49d7a98f TB |
641 | static bool loaded_feature_matches(registered_feature_t *a, |
642 | registered_feature_t *b) | |
a07f2a4b | 643 | { |
49d7a98f TB |
644 | if (plugin_feature_matches(a->feature, b->feature)) |
645 | { | |
2e4d110d | 646 | return b->plugins->find_first(b->plugins, is_feature_loaded, NULL); |
49d7a98f TB |
647 | } |
648 | return FALSE; | |
a07f2a4b MW |
649 | } |
650 | ||
62b9e2f9 | 651 | /** |
49d7a98f | 652 | * Find a loadable module that equals the requested feature |
62b9e2f9 | 653 | */ |
49d7a98f TB |
654 | static bool loadable_feature_equals(registered_feature_t *a, |
655 | registered_feature_t *b) | |
62b9e2f9 | 656 | { |
49d7a98f TB |
657 | if (plugin_feature_equals(a->feature, b->feature)) |
658 | { | |
2e4d110d | 659 | return b->plugins->find_first(b->plugins, is_feature_loadable, NULL); |
49d7a98f TB |
660 | } |
661 | return FALSE; | |
662 | } | |
663 | ||
664 | /** | |
665 | * Find a loadable module that matches the requested feature | |
666 | */ | |
667 | static bool loadable_feature_matches(registered_feature_t *a, | |
668 | registered_feature_t *b) | |
669 | { | |
670 | if (plugin_feature_matches(a->feature, b->feature)) | |
671 | { | |
2e4d110d | 672 | return b->plugins->find_first(b->plugins, is_feature_loadable, NULL); |
49d7a98f TB |
673 | } |
674 | return FALSE; | |
675 | } | |
676 | ||
677 | /** | |
a6691450 | 678 | * Returns a compatible plugin feature for the given dependency |
49d7a98f TB |
679 | */ |
680 | static bool find_compatible_feature(private_plugin_loader_t *this, | |
681 | plugin_feature_t *dependency) | |
682 | { | |
683 | registered_feature_t *feature, lookup = { | |
684 | .feature = dependency, | |
685 | }; | |
686 | ||
687 | feature = this->features->get_match(this->features, &lookup, | |
688 | (void*)loaded_feature_matches); | |
689 | return feature != NULL; | |
690 | } | |
691 | ||
692 | /** | |
693 | * Load a registered plugin feature | |
694 | */ | |
695 | static void load_registered(private_plugin_loader_t *this, | |
696 | registered_feature_t *registered, | |
697 | int level) | |
698 | { | |
699 | enumerator_t *enumerator; | |
700 | provided_feature_t *provided; | |
701 | ||
702 | enumerator = registered->plugins->create_enumerator(registered->plugins); | |
703 | while (enumerator->enumerate(enumerator, &provided)) | |
704 | { | |
705 | load_provided(this, provided, level); | |
706 | } | |
707 | enumerator->destroy(enumerator); | |
708 | } | |
709 | ||
710 | /** | |
711 | * Try to load dependencies of the given feature | |
712 | */ | |
713 | static bool load_dependencies(private_plugin_loader_t *this, | |
714 | provided_feature_t *provided, | |
715 | int level) | |
716 | { | |
717 | registered_feature_t *registered, lookup; | |
62b9e2f9 MW |
718 | int i; |
719 | ||
720 | /* first entry is provided feature, followed by dependencies */ | |
49d7a98f | 721 | for (i = 1; i < provided->dependencies; i++) |
62b9e2f9 | 722 | { |
49d7a98f TB |
723 | if (provided->feature[i].kind != FEATURE_DEPENDS && |
724 | provided->feature[i].kind != FEATURE_SDEPEND) | |
62b9e2f9 MW |
725 | { /* end of dependencies */ |
726 | break; | |
727 | } | |
49d7a98f TB |
728 | |
729 | /* we load the feature even if a compatible one is already loaded, | |
730 | * otherwise e.g. a specific database implementation loaded before | |
731 | * another might cause a plugin feature loaded in-between to fail */ | |
732 | lookup.feature = &provided->feature[i]; | |
733 | do | |
734 | { /* prefer an exactly matching feature, could be omitted but | |
735 | * results in a more predictable behavior */ | |
736 | registered = this->features->get_match(this->features, | |
737 | &lookup, | |
738 | (void*)loadable_feature_equals); | |
739 | if (!registered) | |
740 | { /* try fuzzy matching */ | |
741 | registered = this->features->get_match(this->features, | |
742 | &lookup, | |
743 | (void*)loadable_feature_matches); | |
744 | } | |
745 | if (registered) | |
62b9e2f9 | 746 | { |
49d7a98f TB |
747 | load_registered(this, registered, level); |
748 | } | |
749 | /* we could stop after finding one but for dependencies like | |
750 | * DB_ANY it might be needed to load all matching features */ | |
751 | } | |
752 | while (registered); | |
62b9e2f9 | 753 | |
49d7a98f TB |
754 | if (!find_compatible_feature(this, &provided->feature[i])) |
755 | { | |
49d7a98f TB |
756 | bool soft = provided->feature[i].kind == FEATURE_SDEPEND; |
757 | ||
3963dbbd TB |
758 | #ifndef USE_FUZZING |
759 | char *name, *provide, *depend; | |
ed13c60c | 760 | int indent = level * 2; |
3963dbbd | 761 | |
49d7a98f TB |
762 | name = provided->entry->plugin->get_name(provided->entry->plugin); |
763 | provide = plugin_feature_get_string(&provided->feature[0]); | |
764 | depend = plugin_feature_get_string(&provided->feature[i]); | |
765 | if (soft) | |
766 | { | |
51b9d751 | 767 | DBG3(DBG_LIB, "%*sfeature %s in plugin '%s' has unmet soft " |
49d7a98f TB |
768 | "dependency: %s", indent, "", provide, name, depend); |
769 | } | |
681e53c7 TB |
770 | else if (provided->entry->critical) |
771 | { | |
772 | DBG1(DBG_LIB, "feature %s in critical plugin '%s' has unmet " | |
773 | "dependency: %s", provide, name, depend); | |
774 | } | |
49d7a98f TB |
775 | else |
776 | { | |
51b9d751 | 777 | DBG2(DBG_LIB, "feature %s in plugin '%s' has unmet dependency: " |
49d7a98f TB |
778 | "%s", provide, name, depend); |
779 | } | |
780 | free(provide); | |
781 | free(depend); | |
3963dbbd TB |
782 | #endif /* !USE_FUZZING */ |
783 | ||
49d7a98f TB |
784 | if (soft) |
785 | { /* it's ok if we can't resolve soft dependencies */ | |
786 | continue; | |
62b9e2f9 MW |
787 | } |
788 | return FALSE; | |
789 | } | |
790 | } | |
791 | return TRUE; | |
792 | } | |
793 | ||
794 | /** | |
49d7a98f | 795 | * Load registered plugin features |
62b9e2f9 | 796 | */ |
49d7a98f TB |
797 | static void load_feature(private_plugin_loader_t *this, |
798 | provided_feature_t *provided, | |
799 | int level) | |
62b9e2f9 | 800 | { |
49d7a98f | 801 | if (load_dependencies(this, provided, level)) |
62b9e2f9 | 802 | { |
49d7a98f TB |
803 | if (plugin_feature_load(provided->entry->plugin, provided->feature, |
804 | provided->reg)) | |
805 | { | |
806 | provided->loaded = TRUE; | |
807 | /* insert first so we can unload the features in reverse order */ | |
808 | this->loaded->insert_first(this->loaded, provided); | |
34ee14dd TB |
809 | return; |
810 | } | |
811 | ||
3963dbbd TB |
812 | #ifndef USE_FUZZING |
813 | char *name, *provide; | |
814 | ||
34ee14dd TB |
815 | name = provided->entry->plugin->get_name(provided->entry->plugin); |
816 | provide = plugin_feature_get_string(&provided->feature[0]); | |
817 | if (provided->entry->critical) | |
818 | { | |
819 | DBG1(DBG_LIB, "feature %s in critical plugin '%s' failed to load", | |
820 | provide, name); | |
62b9e2f9 | 821 | } |
49d7a98f | 822 | else |
62b9e2f9 | 823 | { |
34ee14dd TB |
824 | DBG2(DBG_LIB, "feature %s in plugin '%s' failed to load", |
825 | provide, name); | |
62b9e2f9 | 826 | } |
34ee14dd | 827 | free(provide); |
3963dbbd | 828 | #endif /* !USE_FUZZING */ |
62b9e2f9 | 829 | } |
49d7a98f TB |
830 | else |
831 | { /* TODO: we could check the current level and set a different flag when | |
832 | * being loaded as dependency. If there are loops there is a chance the | |
833 | * feature can be loaded later when loading the feature directly. */ | |
34ee14dd | 834 | this->stats.depends++; |
49d7a98f | 835 | } |
34ee14dd TB |
836 | provided->failed = TRUE; |
837 | this->stats.critical += provided->entry->critical ? 1 : 0; | |
838 | this->stats.failed++; | |
62b9e2f9 MW |
839 | } |
840 | ||
841 | /** | |
49d7a98f | 842 | * Load a provided feature |
62b9e2f9 | 843 | */ |
49d7a98f TB |
844 | static void load_provided(private_plugin_loader_t *this, |
845 | provided_feature_t *provided, | |
846 | int level) | |
62b9e2f9 | 847 | { |
62b9e2f9 | 848 | |
49d7a98f TB |
849 | if (provided->loaded || provided->failed) |
850 | { | |
851 | return; | |
852 | } | |
3963dbbd TB |
853 | |
854 | #ifndef USE_FUZZING | |
855 | char *name, *provide; | |
ed13c60c | 856 | int indent = level * 2; |
3963dbbd | 857 | |
49d7a98f TB |
858 | name = provided->entry->plugin->get_name(provided->entry->plugin); |
859 | provide = plugin_feature_get_string(provided->feature); | |
860 | if (provided->loading) | |
861 | { /* prevent loop */ | |
51b9d751 | 862 | DBG3(DBG_LIB, "%*sloop detected while loading %s in plugin '%s'", |
49d7a98f TB |
863 | indent, "", provide, name); |
864 | free(provide); | |
865 | return; | |
866 | } | |
51b9d751 | 867 | DBG3(DBG_LIB, "%*sloading feature %s in plugin '%s'", |
49d7a98f TB |
868 | indent, "", provide, name); |
869 | free(provide); | |
3963dbbd TB |
870 | #else |
871 | if (provided->loading) | |
872 | { | |
873 | return; | |
874 | } | |
875 | #endif /* USE_FUZZING */ | |
49d7a98f TB |
876 | |
877 | provided->loading = TRUE; | |
878 | load_feature(this, provided, level + 1); | |
879 | provided->loading = FALSE; | |
880 | } | |
881 | ||
882 | /** | |
883 | * Load registered plugin features | |
884 | */ | |
885 | static void load_features(private_plugin_loader_t *this) | |
886 | { | |
887 | enumerator_t *enumerator, *inner; | |
888 | plugin_entry_t *plugin; | |
889 | provided_feature_t *provided; | |
890 | ||
891 | /* we do this in plugin order to allow implicit dependencies to be resolved | |
892 | * by reordering plugins */ | |
62b9e2f9 | 893 | enumerator = this->plugins->create_enumerator(this->plugins); |
49d7a98f | 894 | while (enumerator->enumerate(enumerator, &plugin)) |
62b9e2f9 | 895 | { |
49d7a98f TB |
896 | inner = plugin->features->create_enumerator(plugin->features); |
897 | while (inner->enumerate(inner, &provided)) | |
62b9e2f9 | 898 | { |
49d7a98f | 899 | load_provided(this, provided, 0); |
40ca363a | 900 | } |
49d7a98f | 901 | inner->destroy(inner); |
62b9e2f9 MW |
902 | } |
903 | enumerator->destroy(enumerator); | |
62b9e2f9 MW |
904 | } |
905 | ||
2566eb21 TE |
906 | /** |
907 | * Default implementation for plugin feature retrieval | |
908 | */ | |
909 | static int get_features_default(plugin_t *plugin, plugin_feature_t *features[]) | |
910 | { | |
911 | return plugin->get_features(plugin, features); | |
912 | } | |
913 | ||
d965872d | 914 | /** |
49d7a98f | 915 | * Register plugin features provided by the given plugin |
d965872d | 916 | */ |
49d7a98f TB |
917 | static void register_features(private_plugin_loader_t *this, |
918 | plugin_entry_t *entry) | |
d965872d | 919 | { |
49d7a98f TB |
920 | plugin_feature_t *feature, *reg; |
921 | registered_feature_t *registered, lookup; | |
922 | provided_feature_t *provided; | |
923 | int count, i; | |
d965872d | 924 | |
49d7a98f TB |
925 | if (!entry->plugin->get_features) |
926 | { /* feature interface not supported */ | |
927 | DBG1(DBG_LIB, "plugin '%s' does not provide features, deprecated", | |
928 | entry->plugin->get_name(entry->plugin)); | |
929 | return; | |
930 | } | |
931 | reg = NULL; | |
2566eb21 | 932 | count = this->get_features(entry->plugin, &feature); |
d965872d MW |
933 | for (i = 0; i < count; i++) |
934 | { | |
935 | switch (feature->kind) | |
936 | { | |
937 | case FEATURE_PROVIDE: | |
49d7a98f | 938 | lookup.feature = feature; |
d9944102 TB |
939 | registered = this->features->ht.get(&this->features->ht, |
940 | &lookup); | |
49d7a98f | 941 | if (!registered) |
d965872d | 942 | { |
49d7a98f TB |
943 | INIT(registered, |
944 | .feature = feature, | |
945 | .plugins = linked_list_create(), | |
946 | ); | |
d9944102 TB |
947 | this->features->ht.put(&this->features->ht, registered, |
948 | registered); | |
d965872d | 949 | } |
49d7a98f TB |
950 | INIT(provided, |
951 | .entry = entry, | |
952 | .feature = feature, | |
953 | .reg = reg, | |
954 | .dependencies = count - i, | |
955 | ); | |
956 | registered->plugins->insert_last(registered->plugins, | |
957 | provided); | |
958 | entry->features->insert_last(entry->features, provided); | |
d965872d MW |
959 | break; |
960 | case FEATURE_REGISTER: | |
961 | case FEATURE_CALLBACK: | |
962 | reg = feature; | |
963 | break; | |
964 | default: | |
965 | break; | |
966 | } | |
967 | feature++; | |
968 | } | |
49d7a98f TB |
969 | } |
970 | ||
971 | /** | |
972 | * Unregister a plugin feature | |
973 | */ | |
974 | static void unregister_feature(private_plugin_loader_t *this, | |
975 | provided_feature_t *provided) | |
976 | { | |
977 | registered_feature_t *registered, lookup; | |
978 | ||
979 | lookup.feature = provided->feature; | |
d9944102 | 980 | registered = this->features->ht.get(&this->features->ht, &lookup); |
49d7a98f TB |
981 | if (registered) |
982 | { | |
983 | registered->plugins->remove(registered->plugins, provided, NULL); | |
984 | if (registered->plugins->get_count(registered->plugins) == 0) | |
985 | { | |
d9944102 | 986 | this->features->ht.remove(&this->features->ht, &lookup); |
49d7a98f TB |
987 | registered->plugins->destroy(registered->plugins); |
988 | free(registered); | |
989 | } | |
990 | else if (registered->feature == provided->feature) | |
991 | { /* update feature in case the providing plugin gets unloaded */ | |
992 | provided_feature_t *first; | |
993 | ||
994 | registered->plugins->get_first(registered->plugins, (void**)&first); | |
995 | registered->feature = first->feature; | |
996 | } | |
997 | } | |
998 | free(provided); | |
999 | } | |
1000 | ||
1001 | /** | |
1002 | * Unregister plugin features | |
1003 | */ | |
1004 | static void unregister_features(private_plugin_loader_t *this, | |
1005 | plugin_entry_t *entry) | |
1006 | { | |
1007 | provided_feature_t *provided; | |
1008 | enumerator_t *enumerator; | |
1009 | ||
1010 | enumerator = entry->features->create_enumerator(entry->features); | |
1011 | while (enumerator->enumerate(enumerator, &provided)) | |
1012 | { | |
1013 | entry->features->remove_at(entry->features, enumerator); | |
1014 | unregister_feature(this, provided); | |
1015 | } | |
1016 | enumerator->destroy(enumerator); | |
d965872d MW |
1017 | } |
1018 | ||
ef80de60 | 1019 | /** |
49d7a98f | 1020 | * Remove plugins we were not able to load any plugin features from. |
ef80de60 TB |
1021 | */ |
1022 | static void purge_plugins(private_plugin_loader_t *this) | |
1023 | { | |
1024 | enumerator_t *enumerator; | |
1025 | plugin_entry_t *entry; | |
1026 | ||
1027 | enumerator = this->plugins->create_enumerator(this->plugins); | |
1028 | while (enumerator->enumerate(enumerator, &entry)) | |
1029 | { | |
1030 | if (!entry->plugin->get_features) | |
1031 | { /* feature interface not supported */ | |
1032 | continue; | |
1033 | } | |
2e4d110d TB |
1034 | if (!entry->features->find_first(entry->features, is_feature_loaded, |
1035 | NULL)) | |
ef80de60 | 1036 | { |
49d7a98f TB |
1037 | DBG2(DBG_LIB, "unloading plugin '%s' without loaded features", |
1038 | entry->plugin->get_name(entry->plugin)); | |
ef80de60 | 1039 | this->plugins->remove_at(this->plugins, enumerator); |
49d7a98f | 1040 | unregister_features(this, entry); |
ef80de60 TB |
1041 | plugin_entry_destroy(entry); |
1042 | } | |
1043 | } | |
1044 | enumerator->destroy(enumerator); | |
1045 | } | |
1046 | ||
18d21a57 TB |
1047 | METHOD(plugin_loader_t, add_static_features, void, |
1048 | private_plugin_loader_t *this, const char *name, | |
5421092b MW |
1049 | plugin_feature_t features[], int count, bool critical, |
1050 | bool (*reload)(void*), void *reload_data) | |
18d21a57 TB |
1051 | { |
1052 | plugin_entry_t *entry; | |
1053 | plugin_t *plugin; | |
1054 | ||
5421092b | 1055 | plugin = static_features_create(name, features, count, reload, reload_data); |
18d21a57 TB |
1056 | |
1057 | INIT(entry, | |
1058 | .plugin = plugin, | |
1059 | .critical = critical, | |
49d7a98f | 1060 | .features = linked_list_create(), |
18d21a57 TB |
1061 | ); |
1062 | this->plugins->insert_last(this->plugins, entry); | |
49d7a98f | 1063 | register_features(this, entry); |
18d21a57 TB |
1064 | } |
1065 | ||
f2086e42 TB |
1066 | /** |
1067 | * Tries to find the plugin with the given name in the given path. | |
1068 | */ | |
1069 | static bool find_plugin(char *path, char *name, char *buf, char **file) | |
1070 | { | |
1071 | struct stat stb; | |
1072 | ||
1073 | if (path && snprintf(buf, PATH_MAX, "%s/libstrongswan-%s.so", | |
1074 | path, name) < PATH_MAX) | |
1075 | { | |
1076 | if (stat(buf, &stb) == 0) | |
1077 | { | |
1078 | *file = buf; | |
1079 | return TRUE; | |
1080 | } | |
1081 | } | |
1082 | return FALSE; | |
1083 | } | |
1084 | ||
2e4d110d TB |
1085 | CALLBACK(find_plugin_cb, bool, |
1086 | char *path, va_list args) | |
1087 | { | |
1088 | char *name, *buf, **file; | |
1089 | ||
1090 | VA_ARGS_VGET(args, name, buf, file); | |
1091 | return find_plugin(path, name, buf, file); | |
1092 | } | |
1093 | ||
190a2788 TB |
1094 | /** |
1095 | * Used to sort plugins by priority | |
1096 | */ | |
1097 | typedef struct { | |
1098 | /* name of the plugin */ | |
1099 | char *name; | |
1100 | /* the plugins priority */ | |
1101 | int prio; | |
1102 | /* default priority */ | |
1103 | int def; | |
1104 | } plugin_priority_t; | |
1105 | ||
1106 | static void plugin_priority_free(const plugin_priority_t *this, int idx, | |
1107 | void *user) | |
1108 | { | |
1109 | free(this->name); | |
1110 | } | |
1111 | ||
1112 | /** | |
1113 | * Sort plugins and their priority by name | |
1114 | */ | |
1115 | static int plugin_priority_cmp_name(const plugin_priority_t *a, | |
1116 | const plugin_priority_t *b) | |
1117 | { | |
1118 | return strcmp(a->name, b->name); | |
1119 | } | |
1120 | ||
1121 | /** | |
1122 | * Sort plugins by decreasing priority or default priority then by name | |
1123 | */ | |
1124 | static int plugin_priority_cmp(const plugin_priority_t *a, | |
1125 | const plugin_priority_t *b, void *user) | |
1126 | { | |
1127 | int diff; | |
1128 | ||
1129 | diff = b->prio - a->prio; | |
1130 | if (!diff) | |
1131 | { /* the same priority, use default order */ | |
1132 | diff = b->def - a->def; | |
1133 | if (!diff) | |
1134 | { /* same default priority (i.e. both were not found in that list) */ | |
1135 | return strcmp(a->name, b->name); | |
1136 | } | |
1137 | } | |
1138 | return diff; | |
1139 | } | |
1140 | ||
525cc46c TB |
1141 | CALLBACK(plugin_priority_filter, bool, |
1142 | void *null, enumerator_t *orig, va_list args) | |
7c81219b | 1143 | { |
525cc46c TB |
1144 | plugin_priority_t *prio; |
1145 | char **name; | |
1146 | ||
1147 | VA_ARGS_VGET(args, name); | |
1148 | ||
1149 | if (orig->enumerate(orig, &prio)) | |
1150 | { | |
1151 | *name = prio->name; | |
1152 | return TRUE; | |
1153 | } | |
1154 | return FALSE; | |
7c81219b | 1155 | } |
190a2788 TB |
1156 | |
1157 | /** | |
1158 | * Determine the list of plugins to load via load option in each plugin's | |
1159 | * config section. | |
1160 | */ | |
1161 | static char *modular_pluginlist(char *list) | |
1162 | { | |
1163 | enumerator_t *enumerator; | |
1164 | array_t *given, *final; | |
1165 | plugin_priority_t item, *current, found; | |
1166 | char *plugin, *plugins = NULL; | |
1167 | int i = 0, max_prio; | |
7c81219b | 1168 | bool load_def = FALSE; |
190a2788 TB |
1169 | |
1170 | given = array_create(sizeof(plugin_priority_t), 0); | |
1171 | final = array_create(sizeof(plugin_priority_t), 0); | |
1172 | ||
1173 | enumerator = enumerator_create_token(list, " ", " "); | |
1174 | while (enumerator->enumerate(enumerator, &plugin)) | |
1175 | { | |
1176 | item.name = strdup(plugin); | |
1177 | item.prio = i++; | |
1178 | array_insert(given, ARRAY_TAIL, &item); | |
1179 | } | |
1180 | enumerator->destroy(enumerator); | |
1181 | array_sort(given, (void*)plugin_priority_cmp_name, NULL); | |
1182 | /* the maximum priority used for plugins not found in this list */ | |
1183 | max_prio = i + 1; | |
1184 | ||
7c81219b TB |
1185 | if (lib->settings->get_bool(lib->settings, "%s.load_modular", FALSE, |
1186 | lib->ns)) | |
1187 | { | |
1188 | enumerator = lib->settings->create_section_enumerator(lib->settings, | |
190a2788 | 1189 | "%s.plugins", lib->ns); |
7c81219b TB |
1190 | } |
1191 | else | |
1192 | { | |
1193 | enumerator = enumerator_create_filter(array_create_enumerator(given), | |
525cc46c | 1194 | plugin_priority_filter, NULL, NULL); |
7c81219b TB |
1195 | load_def = TRUE; |
1196 | } | |
190a2788 TB |
1197 | while (enumerator->enumerate(enumerator, &plugin)) |
1198 | { | |
1199 | item.prio = lib->settings->get_int(lib->settings, | |
7c81219b | 1200 | "%s.plugins.%s.load", 0, lib->ns, plugin); |
190a2788 TB |
1201 | if (!item.prio) |
1202 | { | |
1203 | if (!lib->settings->get_bool(lib->settings, | |
7c81219b | 1204 | "%s.plugins.%s.load", load_def, lib->ns, plugin)) |
190a2788 TB |
1205 | { |
1206 | continue; | |
1207 | } | |
1208 | item.prio = 1; | |
1209 | } | |
1210 | item.name = plugin; | |
1211 | item.def = max_prio; | |
1212 | if (array_bsearch(given, &item, (void*)plugin_priority_cmp_name, | |
1213 | &found) != -1) | |
1214 | { | |
1215 | item.def = max_prio - found.prio; | |
1216 | } | |
1217 | array_insert(final, ARRAY_TAIL, &item); | |
1218 | } | |
1219 | enumerator->destroy(enumerator); | |
190a2788 TB |
1220 | |
1221 | array_sort(final, (void*)plugin_priority_cmp, NULL); | |
1222 | ||
0ab7d5f1 | 1223 | plugins = strdup(""); |
190a2788 TB |
1224 | enumerator = array_create_enumerator(final); |
1225 | while (enumerator->enumerate(enumerator, ¤t)) | |
1226 | { | |
1227 | char *prev = plugins; | |
1228 | if (asprintf(&plugins, "%s %s", plugins ?: "", current->name) < 0) | |
1229 | { | |
1230 | plugins = prev; | |
1231 | break; | |
1232 | } | |
1233 | free(prev); | |
1234 | } | |
1235 | enumerator->destroy(enumerator); | |
7c81219b | 1236 | array_destroy_function(given, (void*)plugin_priority_free, NULL); |
190a2788 TB |
1237 | array_destroy(final); |
1238 | return plugins; | |
1239 | } | |
1240 | ||
2b363425 | 1241 | METHOD(plugin_loader_t, load_plugins, bool, |
b18a5317 | 1242 | private_plugin_loader_t *this, char *list) |
a3d92a37 | 1243 | { |
11e85517 | 1244 | enumerator_t *enumerator; |
190a2788 | 1245 | char *default_path = NULL, *plugins, *token; |
d6a45127 | 1246 | bool critical_failed = FALSE; |
7daf5226 | 1247 | |
a9f169f6 | 1248 | #ifdef PLUGINDIR |
b18a5317 | 1249 | default_path = PLUGINDIR; |
a9f169f6 | 1250 | #endif /* PLUGINDIR */ |
5b03a350 | 1251 | |
190a2788 TB |
1252 | plugins = modular_pluginlist(list); |
1253 | ||
1254 | enumerator = enumerator_create_token(plugins, " ", " "); | |
d6a45127 | 1255 | while (!critical_failed && enumerator->enumerate(enumerator, &token)) |
a3d92a37 | 1256 | { |
49d7a98f | 1257 | plugin_entry_t *entry; |
d6a45127 | 1258 | bool critical = FALSE; |
a9f169f6 | 1259 | char buf[PATH_MAX], *file = NULL; |
d6a45127 | 1260 | int len; |
7daf5226 | 1261 | |
d6a45127 MW |
1262 | token = strdup(token); |
1263 | len = strlen(token); | |
1264 | if (token[len-1] == '!') | |
1265 | { | |
1266 | critical = TRUE; | |
1267 | token[len-1] = '\0'; | |
1268 | } | |
1535913a MW |
1269 | if (plugin_loaded(this, token)) |
1270 | { | |
1271 | free(token); | |
1272 | continue; | |
1273 | } | |
f2086e42 | 1274 | if (this->paths) |
d6a45127 | 1275 | { |
2e4d110d TB |
1276 | this->paths->find_first(this->paths, find_plugin_cb, NULL, token, |
1277 | buf, &file); | |
f2086e42 TB |
1278 | } |
1279 | if (!file) | |
1280 | { | |
1281 | find_plugin(default_path, token, buf, &file); | |
d6a45127 | 1282 | } |
49d7a98f TB |
1283 | entry = load_plugin(this, token, file, critical); |
1284 | if (entry) | |
1285 | { | |
1286 | register_features(this, entry); | |
1287 | } | |
1288 | else if (critical) | |
d6a45127 | 1289 | { |
62b9e2f9 MW |
1290 | critical_failed = TRUE; |
1291 | DBG1(DBG_LIB, "loading critical plugin '%s' failed", token); | |
552cc11b | 1292 | } |
787b5884 | 1293 | free(token); |
2feed2f3 MW |
1294 | } |
1295 | enumerator->destroy(enumerator); | |
1296 | if (!critical_failed) | |
1297 | { | |
49d7a98f | 1298 | load_features(this); |
0d25c4ef | 1299 | if (this->stats.critical > 0) |
34ee14dd | 1300 | { |
0d25c4ef TB |
1301 | critical_failed = TRUE; |
1302 | DBG1(DBG_LIB, "failed to load %d critical plugin feature%s", | |
1303 | this->stats.critical, this->stats.critical == 1 ? "" : "s"); | |
34ee14dd | 1304 | } |
ef80de60 TB |
1305 | /* unload plugins that we were not able to load any features for */ |
1306 | purge_plugins(this); | |
62b9e2f9 | 1307 | } |
f1ba06c1 TB |
1308 | if (!critical_failed) |
1309 | { | |
1310 | free(this->loaded_plugins); | |
1311 | this->loaded_plugins = loaded_plugins_list(this); | |
1312 | } | |
190a2788 TB |
1313 | if (plugins != list) |
1314 | { | |
1315 | free(plugins); | |
1316 | } | |
d6a45127 | 1317 | return !critical_failed; |
552cc11b MW |
1318 | } |
1319 | ||
49d7a98f TB |
1320 | /** |
1321 | * Unload plugin features, they are registered in reverse order | |
1322 | */ | |
1323 | static void unload_features(private_plugin_loader_t *this) | |
a3d92a37 | 1324 | { |
d965872d | 1325 | enumerator_t *enumerator; |
49d7a98f | 1326 | provided_feature_t *provided; |
62b9e2f9 | 1327 | plugin_entry_t *entry; |
7daf5226 | 1328 | |
49d7a98f TB |
1329 | enumerator = this->loaded->create_enumerator(this->loaded); |
1330 | while (enumerator->enumerate(enumerator, &provided)) | |
a3d92a37 | 1331 | { |
49d7a98f TB |
1332 | entry = provided->entry; |
1333 | plugin_feature_unload(entry->plugin, provided->feature, provided->reg); | |
1334 | this->loaded->remove_at(this->loaded, enumerator); | |
1335 | entry->features->remove(entry->features, provided, NULL); | |
1336 | unregister_feature(this, provided); | |
d965872d | 1337 | } |
49d7a98f TB |
1338 | enumerator->destroy(enumerator); |
1339 | } | |
1340 | ||
1341 | METHOD(plugin_loader_t, unload, void, | |
1342 | private_plugin_loader_t *this) | |
1343 | { | |
1344 | plugin_entry_t *entry; | |
1345 | ||
1346 | /* unload features followed by plugins, in reverse order */ | |
1347 | unload_features(this); | |
1348 | while (this->plugins->remove_last(this->plugins, (void**)&entry) == SUCCESS) | |
d965872d | 1349 | { |
49d7a98f TB |
1350 | if (lib->leak_detective) |
1351 | { /* keep handle to report leaks properly */ | |
1352 | entry->handle = NULL; | |
62b9e2f9 | 1353 | } |
49d7a98f TB |
1354 | unregister_features(this, entry); |
1355 | plugin_entry_destroy(entry); | |
a3d92a37 | 1356 | } |
f1ba06c1 TB |
1357 | free(this->loaded_plugins); |
1358 | this->loaded_plugins = NULL; | |
34ee14dd | 1359 | memset(&this->stats, 0, sizeof(this->stats)); |
a3d92a37 MW |
1360 | } |
1361 | ||
f2086e42 TB |
1362 | METHOD(plugin_loader_t, add_path, void, |
1363 | private_plugin_loader_t *this, char *path) | |
1364 | { | |
1365 | if (!this->paths) | |
1366 | { | |
1367 | this->paths = linked_list_create(); | |
1368 | } | |
1369 | this->paths->insert_last(this->paths, strdupnull(path)); | |
1370 | } | |
1371 | ||
ed49e9a3 MW |
1372 | /** |
1373 | * Reload a plugin by name, NULL for all | |
1374 | */ | |
1375 | static u_int reload_by_name(private_plugin_loader_t *this, char *name) | |
1376 | { | |
1377 | u_int reloaded = 0; | |
1378 | enumerator_t *enumerator; | |
1379 | plugin_t *plugin; | |
1380 | ||
1381 | enumerator = create_plugin_enumerator(this); | |
62b9e2f9 | 1382 | while (enumerator->enumerate(enumerator, &plugin, NULL)) |
ed49e9a3 MW |
1383 | { |
1384 | if (name == NULL || streq(name, plugin->get_name(plugin))) | |
1385 | { | |
62b9e2f9 | 1386 | if (plugin->reload && plugin->reload(plugin)) |
ed49e9a3 MW |
1387 | { |
1388 | DBG2(DBG_LIB, "reloaded configuration of '%s' plugin", | |
1389 | plugin->get_name(plugin)); | |
1390 | reloaded++; | |
1391 | } | |
1392 | } | |
1393 | } | |
1394 | enumerator->destroy(enumerator); | |
1395 | return reloaded; | |
1396 | } | |
1397 | ||
1398 | METHOD(plugin_loader_t, reload, u_int, | |
1399 | private_plugin_loader_t *this, char *list) | |
1400 | { | |
1401 | u_int reloaded = 0; | |
1402 | enumerator_t *enumerator; | |
1403 | char *name; | |
1404 | ||
1405 | if (list == NULL) | |
1406 | { | |
1407 | return reload_by_name(this, NULL); | |
1408 | } | |
1409 | enumerator = enumerator_create_token(list, " ", ""); | |
1410 | while (enumerator->enumerate(enumerator, &name)) | |
1411 | { | |
1412 | reloaded += reload_by_name(this, name); | |
1413 | } | |
1414 | enumerator->destroy(enumerator); | |
1415 | return reloaded; | |
1416 | } | |
1417 | ||
ad1aaf4b TB |
1418 | METHOD(plugin_loader_t, loaded_plugins, char*, |
1419 | private_plugin_loader_t *this) | |
1420 | { | |
f1ba06c1 | 1421 | return this->loaded_plugins ?: ""; |
ad1aaf4b TB |
1422 | } |
1423 | ||
607f8e99 TB |
1424 | METHOD(plugin_loader_t, status, void, |
1425 | private_plugin_loader_t *this, level_t level) | |
1426 | { | |
1427 | if (this->loaded_plugins) | |
1428 | { | |
1429 | dbg(DBG_LIB, level, "loaded plugins: %s", this->loaded_plugins); | |
0d25c4ef TB |
1430 | |
1431 | if (this->stats.failed) | |
1432 | { | |
1bfa5e0c TB |
1433 | DBG2(DBG_LIB, "unable to load %d plugin feature%s (%d due to unmet " |
1434 | "dependencies)", this->stats.failed, | |
1435 | this->stats.failed == 1 ? "" : "s", this->stats.depends); | |
0d25c4ef | 1436 | } |
607f8e99 TB |
1437 | } |
1438 | } | |
1439 | ||
2b363425 MW |
1440 | METHOD(plugin_loader_t, destroy, void, |
1441 | private_plugin_loader_t *this) | |
552cc11b | 1442 | { |
62b9e2f9 | 1443 | unload(this); |
49d7a98f TB |
1444 | this->features->destroy(this->features); |
1445 | this->loaded->destroy(this->loaded); | |
62b9e2f9 | 1446 | this->plugins->destroy(this->plugins); |
f2086e42 | 1447 | DESTROY_FUNCTION_IF(this->paths, free); |
f1ba06c1 | 1448 | free(this->loaded_plugins); |
552cc11b MW |
1449 | free(this); |
1450 | } | |
1451 | ||
1452 | /* | |
1453 | * see header file | |
1454 | */ | |
1455 | plugin_loader_t *plugin_loader_create() | |
1456 | { | |
2b363425 | 1457 | private_plugin_loader_t *this; |
7daf5226 | 1458 | |
2b363425 MW |
1459 | INIT(this, |
1460 | .public = { | |
18d21a57 | 1461 | .add_static_features = _add_static_features, |
6e279171 | 1462 | .load = _load_plugins, |
f2086e42 | 1463 | .add_path = _add_path, |
ed49e9a3 | 1464 | .reload = _reload, |
2b363425 MW |
1465 | .unload = _unload, |
1466 | .create_plugin_enumerator = _create_plugin_enumerator, | |
2bedb0f2 | 1467 | .has_feature = _has_feature, |
ad1aaf4b | 1468 | .loaded_plugins = _loaded_plugins, |
607f8e99 | 1469 | .status = _status, |
2b363425 MW |
1470 | .destroy = _destroy, |
1471 | }, | |
1472 | .plugins = linked_list_create(), | |
49d7a98f | 1473 | .loaded = linked_list_create(), |
d9944102 | 1474 | .features = hashlist_create( |
49d7a98f TB |
1475 | (hashtable_hash_t)registered_feature_hash, |
1476 | (hashtable_equals_t)registered_feature_equals, 64), | |
2566eb21 | 1477 | .get_features = dlsym(RTLD_DEFAULT, "plugin_loader_feature_filter"), |
2b363425 | 1478 | ); |
7daf5226 | 1479 | |
2566eb21 TE |
1480 | if (!this->get_features) |
1481 | { | |
1482 | this->get_features = get_features_default; | |
1483 | } | |
1484 | ||
552cc11b MW |
1485 | return &this->public; |
1486 | } | |
8d2450d8 TB |
1487 | |
1488 | /* | |
1489 | * See header | |
1490 | */ | |
1491 | void plugin_loader_add_plugindirs(char *basedir, char *plugins) | |
1492 | { | |
1493 | enumerator_t *enumerator; | |
1494 | char *name, path[PATH_MAX], dir[64]; | |
1495 | ||
5078f87a | 1496 | enumerator = enumerator_create_token(plugins, " ", "!"); |
8d2450d8 TB |
1497 | while (enumerator->enumerate(enumerator, &name)) |
1498 | { | |
1499 | snprintf(dir, sizeof(dir), "%s", name); | |
1500 | translate(dir, "-", "_"); | |
1501 | snprintf(path, sizeof(path), "%s/%s/.libs", basedir, dir); | |
1502 | lib->plugins->add_path(lib->plugins, path); | |
1503 | } | |
1504 | enumerator->destroy(enumerator); | |
1505 | } |