]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/lib/plugins.c
0b42f905366d7704ff02a31752cd7164f304c4fd
[bacula/bacula] / bacula / src / lib / plugins.c
1 /*
2    Bacula(R) - 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(R) 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    Dmsg0(dbglvl, "load_plugins\n");
105    name_max = pathconf(".", _PC_NAME_MAX);
106    if (name_max < 1024) {
107       name_max = 1024;
108    }
109
110    if (!(dp = opendir(plugin_dir))) {
111       berrno be;
112       Jmsg(NULL, M_ERROR_TERM, 0, _("Failed to open Plugin directory %s: ERR=%s\n"), 
113             plugin_dir, be.bstrerror());
114       Dmsg2(dbglvl, "Failed to open Plugin directory %s: ERR=%s\n", 
115             plugin_dir, be.bstrerror());
116       goto get_out;
117    }
118    
119    len = strlen(plugin_dir);
120    if (len > 0) {
121       need_slash = !IsPathSeparator(plugin_dir[len - 1]);
122    }
123    entry = (struct dirent *)malloc(sizeof(struct dirent) + name_max + 1000);
124    for ( ;; ) {
125       plugin = NULL;            /* Start from a fresh plugin */
126
127       if ((readdir_r(dp, entry, &result) != 0) || (result == NULL)) {
128          if (!found) {
129             Jmsg(NULL, M_WARNING, 0, _("Failed to find any plugins in %s\n"), 
130                   plugin_dir);
131             Dmsg1(dbglvl, "Failed to find any plugins in %s\n", plugin_dir);
132          }
133          break;
134       }
135       if (strcmp(result->d_name, ".") == 0 || 
136           strcmp(result->d_name, "..") == 0) {
137          continue;
138       }
139
140       len = strlen(result->d_name);
141       type_len = strlen(type);
142       if (len < type_len+1 || strcmp(&result->d_name[len-type_len], type) != 0) {
143          Dmsg3(dbglvl, "Rejected plugin: want=%s name=%s len=%d\n", type, result->d_name, len);
144          continue;
145       }
146       Dmsg2(dbglvl, "Found plugin: name=%s len=%d\n", result->d_name, len);
147        
148       pm_strcpy(fname, plugin_dir);
149       if (need_slash) {
150          pm_strcat(fname, "/");
151       }
152       pm_strcat(fname, result->d_name);
153       if (lstat(fname.c_str(), &statp) != 0 || !S_ISREG(statp.st_mode)) {
154          continue;                 /* ignore directories & special files */
155       }
156
157       plugin = new_plugin();
158       plugin->file = bstrdup(result->d_name);
159       plugin->file_len = strstr(plugin->file, type) - plugin->file;
160       plugin->pHandle = dlopen(fname.c_str(), RTLD_NOW);
161       if (!plugin->pHandle) {
162          char *error = (char *)dlerror();
163          Jmsg(NULL, M_ERROR, 0, _("dlopen plugin %s failed: ERR=%s\n"), 
164               fname.c_str(), NPRT(error));
165          Dmsg2(dbglvl, "dlopen plugin %s failed: ERR=%s\n", fname.c_str(), 
166                NPRT(error));
167          close_plugin(plugin);
168          continue;
169       }
170
171       /* Get two global entry points */
172       loadPlugin = (t_loadPlugin)dlsym(plugin->pHandle, "loadPlugin");
173       if (!loadPlugin) {
174          Jmsg(NULL, M_ERROR, 0, _("Lookup of loadPlugin in plugin %s failed: ERR=%s\n"),
175             fname.c_str(), NPRT(dlerror()));
176          Dmsg2(dbglvl, "Lookup of loadPlugin in plugin %s failed: ERR=%s\n", 
177             fname.c_str(), NPRT(dlerror()));
178          close_plugin(plugin);
179          continue;
180       }
181       plugin->unloadPlugin = (t_unloadPlugin)dlsym(plugin->pHandle, "unloadPlugin");
182       if (!plugin->unloadPlugin) {
183          Jmsg(NULL, M_ERROR, 0, _("Lookup of unloadPlugin in plugin %s failed: ERR=%s\n"),
184             fname.c_str(), NPRT(dlerror()));
185          Dmsg2(dbglvl, "Lookup of unloadPlugin in plugin %s failed: ERR=%s\n",
186             fname.c_str(), NPRT(dlerror()));
187          close_plugin(plugin);
188          continue;
189       }
190
191       /* Initialize the plugin */
192       if (loadPlugin(binfo, bfuncs, &plugin->pinfo, &plugin->pfuncs) != bRC_OK) {
193          close_plugin(plugin);
194          continue;
195       }
196       if (!is_plugin_compatible) {
197          Dmsg0(50, "Plugin compatibility pointer not set.\n");   
198       } else if (!is_plugin_compatible(plugin)) {
199          close_plugin(plugin);
200          continue;
201       }
202
203       found = true;                /* found a plugin */
204       bplugin_list->append(plugin);
205    }
206
207 get_out:
208    if (!found && plugin) {
209       close_plugin(plugin);
210    }
211    if (entry) {
212       free(entry);
213    }
214    if (dp) {
215       closedir(dp);
216    }
217    return found;
218 }
219
220 /*
221  * Unload all the loaded plugins 
222  */
223 void unload_plugins()
224 {
225    Plugin *plugin;
226
227    if (!bplugin_list) {
228       return;
229    }
230    foreach_alist(plugin, bplugin_list) {
231       /* Shut it down and unload it */
232       plugin->unloadPlugin();
233       dlclose(plugin->pHandle);
234       if (plugin->file) {
235          free(plugin->file);
236       }
237       free(plugin);
238    }
239    delete bplugin_list;
240    bplugin_list = NULL;
241 }
242
243 /*
244  * Dump plugin information
245  * Each daemon can register a hook that will be called
246  * after a fatal signal.
247  */
248 #define DBG_MAX_HOOK 10
249 static dbg_plugin_hook_t *dbg_plugin_hooks[DBG_MAX_HOOK];
250 static int dbg_plugin_hook_count=0;
251
252 void dbg_plugin_add_hook(dbg_plugin_hook_t *fct)
253 {
254    ASSERT(dbg_plugin_hook_count < DBG_MAX_HOOK);
255    dbg_plugin_hooks[dbg_plugin_hook_count++] = fct;
256 }
257
258 void dbg_print_plugin(FILE *fp)
259 {
260    Plugin *plugin;
261    fprintf(fp, "Attempt to dump plugins. Hook count=%d\n", dbg_plugin_hook_count);
262
263    if (!bplugin_list) {
264       return;
265    }
266    foreach_alist(plugin, bplugin_list) {
267       for(int i=0; i < dbg_plugin_hook_count; i++) {
268 //       dbg_plugin_hook_t *fct = dbg_plugin_hooks[i];
269          fprintf(fp, "Plugin %p name=\"%s\" disabled=%d\n",
270                  plugin, plugin->file, plugin->disabled);
271 //       fct(plugin, fp);
272       }
273    }
274 }