blob: 0d4fccaf3e16e6f0bcac50bf0b4635f6bda85dfa [file] [log] [blame]
Brian Waters13d96012017-12-08 16:53:31 -06001/*********************************************************************************************************
2* Software License Agreement (BSD License) *
3* Author: Sebastien Decugis <sdecugis@freediameter.net> *
4* *
5* Copyright (c) 2013, WIDE Project and NICT *
6* All rights reserved. *
7* *
8* Redistribution and use of this software in source and binary forms, with or without modification, are *
9* permitted provided that the following conditions are met: *
10* *
11* * Redistributions of source code must retain the above *
12* copyright notice, this list of conditions and the *
13* following disclaimer. *
14* *
15* * Redistributions in binary form must reproduce the above *
16* copyright notice, this list of conditions and the *
17* following disclaimer in the documentation and/or other *
18* materials provided with the distribution. *
19* *
20* * Neither the name of the WIDE Project or NICT nor the *
21* names of its contributors may be used to endorse or *
22* promote products derived from this software without *
23* specific prior written permission of WIDE Project and *
24* NICT. *
25* *
26* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED *
27* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
28* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR *
29* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT *
30* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS *
31* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR *
32* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF *
33* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *
34*********************************************************************************************************/
35
36#include "fdcore-internal.h"
37
38#include <dlfcn.h> /* We may use libtool's <ltdl.h> later for better portability.... */
39#include <libgen.h> /* for "basename" */
40
41/* plugins management */
42
43/* List of extensions to load, from the configuration parsing */
44struct fd_ext_info {
45 struct fd_list chain; /* link in the list */
46 char *filename; /* extension filename. must be a dynamic library with fd_ext_init symbol. */
47 char *conffile; /* optional configuration file name for the extension */
48 void *handler; /* object returned by dlopen() */
49 const char **depends; /* names of the other extensions this one depends on (if provided) */
50 char *ext_name; /* points to the extension name, either inside depends, or basename(filename) */
51 int free_ext_name; /* must be freed if it was malloc'd */
52 void (*fini)(void); /* optional address of the fd_ext_fini callback */
53 char *proto_ver;
54 double gen_date;
55};
56
57/* list of extensions */
58static struct fd_list ext_list = FD_LIST_INITIALIZER(ext_list);
59
60/* Add new extension */
61int fd_ext_add( char * filename, char * conffile )
62{
63 struct fd_ext_info * new;
64
65 TRACE_ENTRY("%p %p", filename, conffile);
66
67 /* Check the filename is valid */
68 CHECK_PARAMS( filename );
69
70 /* Create a new object in the list */
71 CHECK_MALLOC( new = malloc( sizeof(struct fd_ext_info) ) );
72 memset(new, 0, sizeof(struct fd_ext_info));
73 fd_list_init(&new->chain, NULL);
74 new->filename = filename;
75 new->conffile = conffile;
76 fd_list_insert_before( &ext_list, &new->chain );
77 TRACE_DEBUG (FULL, "Extension %s added to the list.", filename);
78 return 0;
79}
80
81/* Dump the list */
82DECLARE_FD_DUMP_PROTOTYPE(fd_ext_dump)
83{
84 struct fd_list * li;
85 FD_DUMP_HANDLE_OFFSET();
86
87 if (FD_IS_LIST_EMPTY(&ext_list)) {
88 CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "-none-"), return NULL);
89 } else {
90 for (li = ext_list.next; li != &ext_list; li = li->next)
91 {
92 struct fd_ext_info * ext = (struct fd_ext_info *)li;
93 CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "'%s'[%s], %sloaded%s",
94 ext->filename,
95 ext->conffile?:"(no config file)",
96 ext->handler ? "" : "not ", (li->next == &ext_list) ? "":"\n"), return NULL);
97 }
98 }
99 return *buf;
100}
101
102/* Load the dependencies */
103static int load_dependencies(struct fd_ext_info *ext)
104{
105 TRACE_ENTRY( "%p", ext);
106 /* Attempt to resolve the dependency array */
107 ext->depends = dlsym( ext->handler, "fd_ext_depends");
108 if (!ext->depends) {
109 /* Duplicate the filename */
110 char * tmp = strdup(ext->filename);
111 ext->ext_name = strdup(basename(tmp));
112 free(tmp);
113 ext->free_ext_name = 1;
114 TRACE_DEBUG(FULL, "Old extension's [%s] API: missing dependencies (ignored)", ext->ext_name);
115 return 0;
116 }
117
118 ext->ext_name = (char *)ext->depends[0];
119 return 0;
120
121}
122
123/* Check the dependencies. The object must have been dlopened already. */
124static int check_dependencies(struct fd_ext_info * ext)
125{
126 int i = 1;
127 TRACE_DEBUG(FULL, "Checking dependencies for '%s'...", ext->ext_name);
128
129 while (ext->depends[i]) {
130 struct fd_list * li;
131 for (li = ext_list.next; li != &ext_list; li = li->next)
132 {
133 struct fd_ext_info * e = (struct fd_ext_info *)li;
134 if (!strcasecmp(e->ext_name, ext->depends[i])) {
135 /* the dependency was already loaded */
136 break;
137 }
138 }
139
140 if (li == &ext_list) {
141 /* the dependency was not found */
142 LOG_F("Error: extension [%s] depends on [%s] which was not loaded first. Please fix your configuration file.",
143 ext->ext_name, ext->depends[i]);
144 return ESRCH;
145 }
146
147 i++;
148 }
149
150 /* All dependencies resolved successfully */
151 return 0;
152}
153
154/* Load all extensions in the list */
155int fd_ext_load()
156{
157 int ret;
158 int (*fd_ext_init)(int, int, char *) = NULL;
159 int (*fd_ext_init2)(int, int, char *) = NULL;
160 struct fd_list * li;
161
162 TRACE_ENTRY();
163
164 /* Loop on all extensions */
165 for (li = ext_list.next; li != &ext_list; li = li->next)
166 {
167 struct fd_ext_info * ext = (struct fd_ext_info *)li;
168 LOG_D( "Loading : %s", ext->filename);
169
170 /* Load the extension */
171#ifndef DEBUG
172 ext->handler = dlopen(ext->filename, RTLD_LAZY | RTLD_GLOBAL);
173#else /* DEBUG */
174 /* We resolve symbols immediatly so it's easier to find problems in ABI */
175 ext->handler = dlopen(ext->filename, RTLD_NOW | RTLD_GLOBAL);
176#endif /* DEBUG */
177 if (ext->handler == NULL) {
178 /* An error occured */
179 LOG_F("Loading of extension %s failed: %s", ext->filename, dlerror());
180 ext->handler = dlopen(ext->filename, RTLD_LAZY | RTLD_GLOBAL);
181 if (ext->handler) {
182 if (!check_dependencies(ext)) {
183 LOG_F("In addition, not all declared dependencies are satisfied (Internal Error!)");
184 }
185 }
186 return EINVAL;
187 }
188
189 /* Resolve the entry point of the extension */
190 fd_ext_init = ( int (*) (int, int, char *) )dlsym( ext->handler, "fd_ext_init" );
191
192 if (fd_ext_init == NULL) {
193 /* An error occured */
194 TRACE_ERROR("Unable to resolve symbol 'fd_ext_init' for extension %s: %s", ext->filename, dlerror());
195 return EINVAL;
196 }
197
198
199 /* Resolve the exit point of the extension, which is optional for extensions */
200 ext->fini = ( void (*) (void) )dlsym( ext->handler, "fd_ext_fini" );
201
202 if (ext->fini == NULL) {
203 /* Not provided */
204 TRACE_DEBUG (FULL, "Extension [%s] has no fd_ext_fini function.", ext->filename);
205 } else {
206 /* Provided */
207 TRACE_DEBUG (FULL, "Extension [%s] fd_ext_fini has been resolved successfully.", ext->filename);
208 }
209
210 /* Now call the entry point to initialize the extension */
211 ret = (*fd_ext_init)( FD_PROJECT_VERSION_MAJOR, FD_PROJECT_VERSION_MINOR, ext->conffile );
212 if (ret != 0) {
213 /* The extension was unable to load cleanly */
214 TRACE_ERROR("Extension %s returned an error during initialization: %s", ext->filename, strerror(ret));
215 return ret;
216 }
217
218 /* Proceed to the next extension */
219 }
220
221 for( li = ext_list.next; li != &ext_list; li = li->next )
222 {
223 struct fd_ext_info * ext = (struct fd_ext_info *)li;
224 CHECK_FCT( load_dependencies(ext));
225 }
226
227
228 for( li = ext_list.next; li != &ext_list; li = li->next )
229 {
230 struct fd_ext_info * ext = (struct fd_ext_info *)li;
231
232 /* Check if declared dependencies are satisfied. */
233 CHECK_FCT( check_dependencies(ext) );
234
235 /* Loading methods init2 & parserules */
236 fd_ext_init2 = ( int (*) (int, int, char *) )dlsym( ext->handler, "fd_ext_init2" );
237
238 if (fd_ext_init2 == NULL) {
239 /* Old extensions do not define fd_ext_init2 */
240 LOG_N("Unable to resolve symbol 'fd_ext_init2' for extension %s: ", ext->filename);
241 }
242 else {
243 ret = (*fd_ext_init2)( FD_PROJECT_VERSION_MAJOR, FD_PROJECT_VERSION_MINOR, ext->conffile );
244 if (ret != 0) {
245 /* The extension was unable to load cleanly */
246 TRACE_ERROR("Extension %s returned an error during parse local rules: %s", ext->filename, strerror(ret));
247 return ret;
248 }
249 }
250 }
251
252 LOG_N("All extensions loaded.");
253
254 /* We have finished. */
255 return 0;
256}
257
258/* Now unload the extensions and free the memory */
259int fd_ext_term( void )
260{
261 TRACE_ENTRY();
262
263 /* Loop on all extensions, in FIFO order */
264 while (!FD_IS_LIST_EMPTY(&ext_list))
265 {
266 struct fd_list * li = ext_list.next;
267 struct fd_ext_info * ext = (struct fd_ext_info *)li;
268
269 /* Unlink this element from the list */
270 fd_list_unlink(li);
271
272 /* Call the exit point of the extension, if it was resolved */
273 if (ext->fini != NULL) {
274 TRACE_DEBUG (FULL, "Calling [%s]->fd_ext_fini function.", ext->ext_name ?: ext->filename);
275 (*ext->fini)();
276 }
277
278#ifndef SKIP_DLCLOSE
279 /* Now unload the extension */
280 if (ext->handler) {
281 TRACE_DEBUG (FULL, "Unloading %s", ext->ext_name ?: ext->filename);
282 if ( dlclose(ext->handler) != 0 ) {
283 TRACE_DEBUG (INFO, "Unloading [%s] failed : %s", ext->ext_name ?: ext->filename, dlerror());
284 }
285 }
286#endif /* SKIP_DLCLOSE */
287
288 /* Free the object and continue */
289 if (ext->free_ext_name)
290 free(ext->ext_name);
291 free(ext->filename);
292 free(ext->conffile);
293 free(ext);
294 }
295
296 /* We always return 0 since we would not handle an error anyway... */
297 return 0;
298}
299