Load plugins only once, even if listed twice
[strongswan.git] / src / libstrongswan / plugins / plugin_loader.c
1 /*
2 * Copyright (C) 2010 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 <integrity_checker.h>
27 #include <utils/linked_list.h>
28 #include <plugins/plugin.h>
29
30 typedef struct private_plugin_loader_t private_plugin_loader_t;
31
32 /**
33 * private data of plugin_loader
34 */
35 struct private_plugin_loader_t {
36
37 /**
38 * public functions
39 */
40 plugin_loader_t public;
41
42 /**
43 * list of loaded plugins
44 */
45 linked_list_t *plugins;
46
47 /**
48 * names of loaded plugins
49 */
50 linked_list_t *names;
51 };
52
53 #ifdef MONOLITHIC
54 /**
55 * load a single plugin in monolithic mode
56 */
57 static plugin_t* load_plugin(private_plugin_loader_t *this,
58 char *path, char *name)
59 {
60 char create[128];
61 plugin_t *plugin;
62 plugin_constructor_t constructor;
63
64 if (snprintf(create, sizeof(create), "%s_plugin_create",
65 name) >= sizeof(create))
66 {
67 return NULL;
68 }
69 translate(create, "-", "_");
70 constructor = dlsym(RTLD_DEFAULT, create);
71 if (constructor == NULL)
72 {
73 DBG1(DBG_LIB, "plugin '%s': failed to load - %s not found", name,
74 create);
75 return NULL;
76 }
77 plugin = constructor();
78 if (plugin == NULL)
79 {
80 DBG1(DBG_LIB, "plugin '%s': failed to load - %s returned NULL", name,
81 create);
82 return NULL;
83 }
84 DBG2(DBG_LIB, "plugin '%s': loaded successfully", name);
85
86 return plugin;
87 }
88 #else
89 /**
90 * load a single plugin
91 */
92 static plugin_t* load_plugin(private_plugin_loader_t *this,
93 char *path, char *name)
94 {
95 char create[128];
96 char file[PATH_MAX];
97 void *handle;
98 plugin_t *plugin;
99 plugin_constructor_t constructor;
100
101 if (snprintf(file, sizeof(file), "%s/libstrongswan-%s.so", path,
102 name) >= sizeof(file) ||
103 snprintf(create, sizeof(create), "%s_plugin_create",
104 name) >= sizeof(create))
105 {
106 return NULL;
107 }
108 translate(create, "-", "_");
109 if (lib->integrity)
110 {
111 if (!lib->integrity->check_file(lib->integrity, name, file))
112 {
113 DBG1(DBG_LIB, "plugin '%s': failed file integrity test of '%s'",
114 name, file);
115 return NULL;
116 }
117 }
118 handle = dlopen(file, RTLD_LAZY);
119 if (handle == NULL)
120 {
121 DBG1(DBG_LIB, "plugin '%s' failed to load: %s", name, dlerror());
122 return NULL;
123 }
124 constructor = dlsym(handle, create);
125 if (constructor == NULL)
126 {
127 DBG1(DBG_LIB, "plugin '%s': failed to load - %s not found", name,
128 create);
129 dlclose(handle);
130 return NULL;
131 }
132 if (lib->integrity)
133 {
134 if (!lib->integrity->check_segment(lib->integrity, name, constructor))
135 {
136 DBG1(DBG_LIB, "plugin '%s': failed segment integrity test", name);
137 dlclose(handle);
138 return NULL;
139 }
140 DBG1(DBG_LIB, "plugin '%s': passed file and segment integrity tests",
141 name);
142 }
143 plugin = constructor();
144 if (plugin == NULL)
145 {
146 DBG1(DBG_LIB, "plugin '%s': failed to load - %s returned NULL", name,
147 create);
148 dlclose(handle);
149 return NULL;
150 }
151 DBG2(DBG_LIB, "plugin '%s': loaded successfully", name);
152
153 /* we do not store or free dlopen() handles, leak_detective requires
154 * the modules to keep loaded until leak report */
155 return plugin;
156 }
157 #endif
158
159 /**
160 * Check if a plugin is already loaded
161 */
162 static bool plugin_loaded(private_plugin_loader_t *this, char *name)
163 {
164 enumerator_t *enumerator;
165 bool found = FALSE;
166 char *current;
167
168 enumerator = this->names->create_enumerator(this->names);
169 while (enumerator->enumerate(enumerator, &current))
170 {
171 if (streq(name, current))
172 {
173 found = TRUE;
174 break;
175 }
176 }
177 enumerator->destroy(enumerator);
178 return found;
179 }
180
181 /**
182 * Implementation of plugin_loader_t.load_plugins.
183 */
184 static bool load(private_plugin_loader_t *this, char *path, char *list)
185 {
186 enumerator_t *enumerator;
187 char *token;
188 bool critical_failed = FALSE;
189
190 #ifndef MONOLITHIC
191 if (path == NULL)
192 {
193 path = PLUGINDIR;
194 }
195 #endif
196
197 enumerator = enumerator_create_token(list, " ", " ");
198 while (!critical_failed && enumerator->enumerate(enumerator, &token))
199 {
200 plugin_t *plugin;
201 bool critical = FALSE;
202 int len;
203
204 token = strdup(token);
205 len = strlen(token);
206 if (token[len-1] == '!')
207 {
208 critical = TRUE;
209 token[len-1] = '\0';
210 }
211 if (plugin_loaded(this, token))
212 {
213 free(token);
214 continue;
215 }
216 plugin = load_plugin(this, path, token);
217 if (plugin)
218 {
219 this->plugins->insert_last(this->plugins, plugin);
220 this->names->insert_last(this->names, token);
221 }
222 else
223 {
224 if (critical)
225 {
226 critical_failed = TRUE;
227 DBG1(DBG_LIB, "loading critical plugin '%s' failed", token);
228 }
229 free(token);
230 }
231 }
232 enumerator->destroy(enumerator);
233 return !critical_failed;
234 }
235
236 /**
237 * Implementation of plugin_loader_t.unload
238 */
239 static void unload(private_plugin_loader_t *this)
240 {
241 plugin_t *plugin;
242 char *name;
243
244 /* unload plugins in reverse order */
245 while (this->plugins->remove_last(this->plugins,
246 (void**)&plugin) == SUCCESS)
247 {
248 plugin->destroy(plugin);
249 }
250 while (this->names->remove_last(this->names, (void**)&name) == SUCCESS)
251 {
252 free(name);
253 }
254 }
255
256 /**
257 * Implementation of plugin_loader_t.create_plugin_enumerator
258 */
259 static enumerator_t* create_plugin_enumerator(private_plugin_loader_t *this)
260 {
261 return this->names->create_enumerator(this->names);
262 }
263
264 /**
265 * Implementation of plugin_loader_t.destroy
266 */
267 static void destroy(private_plugin_loader_t *this)
268 {
269 this->plugins->destroy_offset(this->plugins, offsetof(plugin_t, destroy));
270 this->names->destroy_function(this->names, free);
271 free(this);
272 }
273
274 /*
275 * see header file
276 */
277 plugin_loader_t *plugin_loader_create()
278 {
279 private_plugin_loader_t *this = malloc_thing(private_plugin_loader_t);
280
281 this->public.load = (bool(*)(plugin_loader_t*, char *path, char *prefix))load;
282 this->public.unload = (void(*)(plugin_loader_t*))unload;
283 this->public.create_plugin_enumerator = (enumerator_t*(*)(plugin_loader_t*))create_plugin_enumerator;
284 this->public.destroy = (void(*)(plugin_loader_t*))destroy;
285
286 this->plugins = linked_list_create();
287 this->names = linked_list_create();
288
289 return &this->public;
290 }
291