]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/lib/plugins.c
Fix bug #1764 plugin_list shadows global variable of mysql 5.5
[bacula/bacula] / bacula / src / lib / plugins.c
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2007-2011 Free Software Foundation Europe e.V.
5
6    The main author of Bacula is Kern Sibbald, with contributions from
7    many others, a complete list can be found in the file AUTHORS.
8    This program is Free Software; you can redistribute it and/or
9    modify it under the terms of version three of the GNU Affero General Public
10    License as published by the Free Software Foundation, which is 
11    listed in the file LICENSE.
12
13    This program is distributed in the hope that it will be useful, but
14    WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16    General Public License for more details.
17
18    You should have received a copy of the GNU Affero General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21    02110-1301, USA.
22
23    Bacula® is a registered trademark of Kern Sibbald.
24    The licensor of Bacula is the Free Software Foundation Europe
25    (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26    Switzerland, email:ftf@fsfeurope.org.
27 */
28 /*
29  *    Plugin load/unloader for all Bacula daemons
30  *
31  * Kern Sibbald, October 2007
32  */
33
34 #include "bacula.h"
35 #include <dlfcn.h>
36 #ifdef HAVE_DIRENT_H
37 #include <dirent.h>
38 #define NAMELEN(dirent) (strlen((dirent)->d_name))
39 #endif
40 #ifndef HAVE_READDIR_R
41 int readdir_r(DIR *dirp, struct dirent *entry, struct dirent **result);
42 #endif
43
44 #ifndef RTLD_NOW
45 #define RTLD_NOW 2
46 #endif
47
48 #include "plugins.h"
49
50 static const int dbglvl = 50;
51
52 /* All loaded plugins */
53 alist *bplugin_list = NULL;
54
55 /*
56  * Create a new plugin "class" entry and enter it in the
57  *  list of plugins.  Note, this is not the same as
58  *  an instance of the plugin. 
59  */
60 Plugin *new_plugin()
61 {
62    Plugin *plugin;
63
64    plugin = (Plugin *)malloc(sizeof(Plugin));
65    memset(plugin, 0, sizeof(Plugin));
66    return plugin;
67 }
68
69 static void close_plugin(Plugin *plugin)
70 {
71    if (plugin->file) {
72       Dmsg1(50, "Got plugin=%s but not accepted.\n", plugin->file);
73    }
74    if (plugin->unloadPlugin) {
75       plugin->unloadPlugin();
76    }
77    if (plugin->pHandle) {
78       dlclose(plugin->pHandle);
79    }
80    if (plugin->file) {
81       free(plugin->file);
82    }
83    free(plugin);
84 }
85
86 /*
87  * Load all the plugins in the specified directory.
88  */
89 bool load_plugins(void *binfo, void *bfuncs, const char *plugin_dir, 
90         const char *type, bool is_plugin_compatible(Plugin *plugin))
91 {
92    bool found = false;
93    t_loadPlugin loadPlugin;
94    Plugin *plugin = NULL;
95    DIR* dp = NULL;
96    struct dirent *entry = NULL, *result;
97    int name_max;
98    struct stat statp;
99    POOL_MEM fname(PM_FNAME);
100    bool need_slash = false;
101    int len, type_len;
102
103
104    name_max = pathconf(".", _PC_NAME_MAX);
105    if (name_max < 1024) {
106       name_max = 1024;
107    }
108
109    if (!(dp = opendir(plugin_dir))) {
110       berrno be;
111       Jmsg(NULL, M_ERROR_TERM, 0, _("Failed to open Plugin directory %s: ERR=%s\n"), 
112             plugin_dir, be.bstrerror());
113       Dmsg2(dbglvl, "Failed to open Plugin directory %s: ERR=%s\n", 
114             plugin_dir, be.bstrerror());
115       goto get_out;
116    }
117    
118    len = strlen(plugin_dir);
119    if (len > 0) {
120       need_slash = !IsPathSeparator(plugin_dir[len - 1]);
121    }
122    entry = (struct dirent *)malloc(sizeof(struct dirent) + name_max + 1000);
123    for ( ;; ) {
124       plugin = NULL;            /* Start from a fresh plugin */
125
126       if ((readdir_r(dp, entry, &result) != 0) || (result == NULL)) {
127          if (!found) {
128             Jmsg(NULL, M_WARNING, 0, _("Failed to find any plugins in %s\n"), 
129                   plugin_dir);
130             Dmsg1(dbglvl, "Failed to find any plugins in %s\n", plugin_dir);
131          }
132          break;
133       }
134       if (strcmp(result->d_name, ".") == 0 || 
135           strcmp(result->d_name, "..") == 0) {
136          continue;
137       }
138
139       len = strlen(result->d_name);
140       type_len = strlen(type);
141       if (len < type_len+1 || strcmp(&result->d_name[len-type_len], type) != 0) {
142          Dmsg3(dbglvl, "Rejected plugin: want=%s name=%s len=%d\n", type, result->d_name, len);
143          continue;
144       }
145       Dmsg2(dbglvl, "Loaded plugin: name=%s len=%d\n", result->d_name, len);
146        
147       pm_strcpy(fname, plugin_dir);
148       if (need_slash) {
149          pm_strcat(fname, "/");
150       }
151       pm_strcat(fname, result->d_name);
152       if (lstat(fname.c_str(), &statp) != 0 || !S_ISREG(statp.st_mode)) {
153          continue;                 /* ignore directories & special files */
154       }
155
156       plugin = new_plugin();
157       plugin->file = bstrdup(result->d_name);
158       plugin->file_len = strstr(plugin->file, type) - plugin->file;
159       plugin->pHandle = dlopen(fname.c_str(), RTLD_NOW);
160       if (!plugin->pHandle) {
161          Jmsg(NULL, M_ERROR, 0, _("Plugin load %s failed: ERR=%s\n"), 
162               fname.c_str(), NPRT(dlerror()));
163          Dmsg2(dbglvl, "Plugin load %s failed: ERR=%s\n", fname.c_str(), 
164                NPRT(dlerror()));
165          close_plugin(plugin);
166          continue;
167       }
168
169       /* Get two global entry points */
170       loadPlugin = (t_loadPlugin)dlsym(plugin->pHandle, "loadPlugin");
171       if (!loadPlugin) {
172          Jmsg(NULL, M_ERROR, 0, _("Lookup of loadPlugin in plugin %s failed: ERR=%s\n"),
173             fname.c_str(), NPRT(dlerror()));
174          Dmsg2(dbglvl, "Lookup of loadPlugin in plugin %s failed: ERR=%s\n", 
175             fname.c_str(), NPRT(dlerror()));
176          close_plugin(plugin);
177          continue;
178       }
179       plugin->unloadPlugin = (t_unloadPlugin)dlsym(plugin->pHandle, "unloadPlugin");
180       if (!plugin->unloadPlugin) {
181          Jmsg(NULL, M_ERROR, 0, _("Lookup of unloadPlugin in plugin %s failed: ERR=%s\n"),
182             fname.c_str(), NPRT(dlerror()));
183          Dmsg2(dbglvl, "Lookup of unloadPlugin in plugin %s failed: ERR=%s\n",
184             fname.c_str(), NPRT(dlerror()));
185          close_plugin(plugin);
186          continue;
187       }
188
189       /* Initialize the plugin */
190       if (loadPlugin(binfo, bfuncs, &plugin->pinfo, &plugin->pfuncs) != bRC_OK) {
191          close_plugin(plugin);
192          continue;
193       }
194       if (!is_plugin_compatible) {
195          Dmsg0(50, "Plugin compatibility pointer not set.\n");   
196       } else if (!is_plugin_compatible(plugin)) {
197          close_plugin(plugin);
198          continue;
199       }
200
201       found = true;                /* found a plugin */
202       bplugin_list->append(plugin);
203    }
204
205 get_out:
206    if (!found && plugin) {
207       close_plugin(plugin);
208    }
209    if (entry) {
210       free(entry);
211    }
212    if (dp) {
213       closedir(dp);
214    }
215    return found;
216 }
217
218 /*
219  * Unload all the loaded plugins 
220  */
221 void unload_plugins()
222 {
223    Plugin *plugin;
224
225    if (!bplugin_list) {
226       return;
227    }
228    foreach_alist(plugin, bplugin_list) {
229       /* Shut it down and unload it */
230       plugin->unloadPlugin();
231       dlclose(plugin->pHandle);
232       if (plugin->file) {
233          free(plugin->file);
234       }
235       free(plugin);
236    }
237    delete bplugin_list;
238    bplugin_list = NULL;
239 }
240
241 /*
242  * Dump plugin information
243  * Each daemon can register a hook that will be called
244  * after a fatal signal.
245  */
246 #define DBG_MAX_HOOK 10
247 static dbg_plugin_hook_t *dbg_plugin_hooks[DBG_MAX_HOOK];
248 static int dbg_plugin_hook_count=0;
249
250 void dbg_plugin_add_hook(dbg_plugin_hook_t *fct)
251 {
252    ASSERT(dbg_plugin_hook_count < DBG_MAX_HOOK);
253    dbg_plugin_hooks[dbg_plugin_hook_count++] = fct;
254 }
255
256 void dbg_print_plugin(FILE *fp)
257 {
258    Plugin *plugin;
259    fprintf(fp, "Attempt to dump plugins. Hook count=%d\n", dbg_plugin_hook_count);
260
261    if (!bplugin_list) {
262       return;
263    }
264    foreach_alist(plugin, bplugin_list) {
265       for(int i=0; i < dbg_plugin_hook_count; i++) {
266 //       dbg_plugin_hook_t *fct = dbg_plugin_hooks[i];
267          fprintf(fp, "Plugin %p name=\"%s\" disabled=%d\n",
268                  plugin, plugin->file, plugin->disabled);
269 //       fct(plugin, fp);
270       }
271    }
272 }