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