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