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