]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/lib/plugins.c
0cffff1194d35dfa780576756b6dd12cc102c328
[bacula/bacula] / bacula / src / lib / plugins.c
1 /*
2    Bacula(R) - The Network Backup Solution
3
4    Copyright (C) 2007-2012 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 /* 
53  * List of all loaded plugins.
54  *
55  * NOTE!!! This is a global do not try walking it with
56  *   foreach_alist, you must use foreach_alist_index !!!!!!
57  */
58 alist *bplugin_list = NULL;
59
60 /*
61  * Create a new plugin "class" entry and enter it in the
62  *  list of plugins.  Note, this is not the same as
63  *  an instance of the plugin. 
64  */
65 Plugin *new_plugin()
66 {
67    Plugin *plugin;
68
69    plugin = (Plugin *)malloc(sizeof(Plugin));
70    memset(plugin, 0, sizeof(Plugin));
71    return plugin;
72 }
73
74 static void close_plugin(Plugin *plugin)
75 {
76    if (plugin->file) {
77       Dmsg1(50, "Got plugin=%s but not accepted.\n", plugin->file);
78    }
79    if (plugin->unloadPlugin) {
80       plugin->unloadPlugin();
81    }
82    if (plugin->pHandle) {
83       dlclose(plugin->pHandle);
84    }
85    if (plugin->file) {
86       free(plugin->file);
87    }
88    free(plugin);
89 }
90
91 /*
92  * Load all the plugins in the specified directory.
93  */
94 bool load_plugins(void *binfo, void *bfuncs, const char *plugin_dir, 
95         const char *type, bool is_plugin_compatible(Plugin *plugin))
96 {
97    bool found = false;
98    t_loadPlugin loadPlugin;
99    Plugin *plugin = NULL;
100    DIR* dp = NULL;
101    struct dirent *entry = NULL, *result;
102    int name_max;
103    struct stat statp;
104    POOL_MEM fname(PM_FNAME);
105    bool need_slash = false;
106    int len, type_len;
107
108
109    Dmsg0(dbglvl, "load_plugins\n");
110    name_max = pathconf(".", _PC_NAME_MAX);
111    if (name_max < 1024) {
112       name_max = 1024;
113    }
114
115    if (!(dp = opendir(plugin_dir))) {
116       berrno be;
117       Jmsg(NULL, M_ERROR_TERM, 0, _("Failed to open Plugin directory %s: ERR=%s\n"), 
118             plugin_dir, be.bstrerror());
119       Dmsg2(dbglvl, "Failed to open Plugin directory %s: ERR=%s\n", 
120             plugin_dir, be.bstrerror());
121       goto get_out;
122    }
123    
124    len = strlen(plugin_dir);
125    if (len > 0) {
126       need_slash = !IsPathSeparator(plugin_dir[len - 1]);
127    }
128    entry = (struct dirent *)malloc(sizeof(struct dirent) + name_max + 1000);
129    for ( ;; ) {
130       plugin = NULL;            /* Start from a fresh plugin */
131
132       if ((readdir_r(dp, entry, &result) != 0) || (result == NULL)) {
133          if (!found) {
134             Jmsg(NULL, M_WARNING, 0, _("Failed to find any plugins in %s\n"), 
135                   plugin_dir);
136             Dmsg1(dbglvl, "Failed to find any plugins in %s\n", plugin_dir);
137          }
138          break;
139       }
140       if (strcmp(result->d_name, ".") == 0 || 
141           strcmp(result->d_name, "..") == 0) {
142          continue;
143       }
144
145       len = strlen(result->d_name);
146       type_len = strlen(type);
147       if (len < type_len+1 || strcmp(&result->d_name[len-type_len], type) != 0) {
148          Dmsg3(dbglvl, "Rejected plugin: want=%s name=%s len=%d\n", type, result->d_name, len);
149          continue;
150       }
151       Dmsg2(dbglvl, "Found plugin: name=%s len=%d\n", result->d_name, len);
152        
153       pm_strcpy(fname, plugin_dir);
154       if (need_slash) {
155          pm_strcat(fname, "/");
156       }
157       pm_strcat(fname, result->d_name);
158       if (lstat(fname.c_str(), &statp) != 0 || !S_ISREG(statp.st_mode)) {
159          continue;                 /* ignore directories & special files */
160       }
161
162       plugin = new_plugin();
163       plugin->file = bstrdup(result->d_name);
164       plugin->file_len = strstr(plugin->file, type) - plugin->file;
165       plugin->pHandle = dlopen(fname.c_str(), RTLD_NOW);
166       if (!plugin->pHandle) {
167          char *error = (char *)dlerror();
168          Jmsg(NULL, M_ERROR, 0, _("dlopen plugin %s failed: ERR=%s\n"), 
169               fname.c_str(), NPRT(error));
170          Dmsg2(dbglvl, "dlopen plugin %s failed: ERR=%s\n", fname.c_str(), 
171                NPRT(error));
172          close_plugin(plugin);
173          continue;
174       }
175
176       /* Get two global entry points */
177       loadPlugin = (t_loadPlugin)dlsym(plugin->pHandle, "loadPlugin");
178       if (!loadPlugin) {
179          Jmsg(NULL, M_ERROR, 0, _("Lookup of loadPlugin in plugin %s failed: ERR=%s\n"),
180             fname.c_str(), NPRT(dlerror()));
181          Dmsg2(dbglvl, "Lookup of loadPlugin in plugin %s failed: ERR=%s\n", 
182             fname.c_str(), NPRT(dlerror()));
183          close_plugin(plugin);
184          continue;
185       }
186       plugin->unloadPlugin = (t_unloadPlugin)dlsym(plugin->pHandle, "unloadPlugin");
187       if (!plugin->unloadPlugin) {
188          Jmsg(NULL, M_ERROR, 0, _("Lookup of unloadPlugin in plugin %s failed: ERR=%s\n"),
189             fname.c_str(), NPRT(dlerror()));
190          Dmsg2(dbglvl, "Lookup of unloadPlugin in plugin %s failed: ERR=%s\n",
191             fname.c_str(), NPRT(dlerror()));
192          close_plugin(plugin);
193          continue;
194       }
195
196       /* Initialize the plugin */
197       if (loadPlugin(binfo, bfuncs, &plugin->pinfo, &plugin->pfuncs) != bRC_OK) {
198          close_plugin(plugin);
199          continue;
200       }
201       if (!is_plugin_compatible) {
202          Dmsg0(50, "Plugin compatibility pointer not set.\n");   
203       } else if (!is_plugin_compatible(plugin)) {
204          close_plugin(plugin);
205          continue;
206       }
207
208       found = true;                /* found a plugin */
209       bplugin_list->append(plugin);
210    }
211
212 get_out:
213    if (!found && plugin) {
214       close_plugin(plugin);
215    }
216    if (entry) {
217       free(entry);
218    }
219    if (dp) {
220       closedir(dp);
221    }
222    return found;
223 }
224
225 /*
226  * Unload all the loaded plugins 
227  */
228 void unload_plugins()
229 {
230    Plugin *plugin;
231
232    if (!bplugin_list) {
233       return;
234    }
235    foreach_alist(plugin, bplugin_list) {
236       /* Shut it down and unload it */
237       plugin->unloadPlugin();
238       dlclose(plugin->pHandle);
239       if (plugin->file) {
240          free(plugin->file);
241       }
242       free(plugin);
243    }
244    delete bplugin_list;
245    bplugin_list = NULL;
246 }
247
248 /*
249  * Dump plugin information
250  * Each daemon can register a hook that will be called
251  * after a fatal signal.
252  */
253 #define DBG_MAX_HOOK 10
254 static dbg_plugin_hook_t *dbg_plugin_hooks[DBG_MAX_HOOK];
255 static int dbg_plugin_hook_count=0;
256
257 void dbg_plugin_add_hook(dbg_plugin_hook_t *fct)
258 {
259    ASSERT(dbg_plugin_hook_count < DBG_MAX_HOOK);
260    dbg_plugin_hooks[dbg_plugin_hook_count++] = fct;
261 }
262
263 void dbg_print_plugin(FILE *fp)
264 {
265    Plugin *plugin;
266    fprintf(fp, "Attempt to dump plugins. Hook count=%d\n", dbg_plugin_hook_count);
267
268    if (!bplugin_list) {
269       return;
270    }
271    foreach_alist(plugin, bplugin_list) {
272       for(int i=0; i < dbg_plugin_hook_count; i++) {
273 //       dbg_plugin_hook_t *fct = dbg_plugin_hooks[i];
274          fprintf(fp, "Plugin %p name=\"%s\" disabled=%d\n",
275                  plugin, plugin->file, plugin->disabled);
276 //       fct(plugin, fp);
277       }
278    }
279 }