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