]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/lib/plugins.c
Switch from GPLv2 to AGPLv3
[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 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 *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 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->pHandle = dlopen(fname.c_str(), RTLD_NOW);
159       if (!plugin->pHandle) {
160          Jmsg(NULL, M_ERROR, 0, _("Plugin load %s failed: ERR=%s\n"), 
161               fname.c_str(), NPRT(dlerror()));
162          Dmsg2(dbglvl, "Plugin load %s failed: ERR=%s\n", fname.c_str(), 
163                NPRT(dlerror()));
164          close_plugin(plugin);
165          continue;
166       }
167
168       /* Get two global entry points */
169       loadPlugin = (t_loadPlugin)dlsym(plugin->pHandle, "loadPlugin");
170       if (!loadPlugin) {
171          Jmsg(NULL, M_ERROR, 0, _("Lookup of loadPlugin in plugin %s failed: ERR=%s\n"),
172             fname.c_str(), NPRT(dlerror()));
173          Dmsg2(dbglvl, "Lookup of loadPlugin in plugin %s failed: ERR=%s\n", 
174             fname.c_str(), NPRT(dlerror()));
175          close_plugin(plugin);
176          continue;
177       }
178       plugin->unloadPlugin = (t_unloadPlugin)dlsym(plugin->pHandle, "unloadPlugin");
179       if (!plugin->unloadPlugin) {
180          Jmsg(NULL, M_ERROR, 0, _("Lookup of unloadPlugin in plugin %s failed: ERR=%s\n"),
181             fname.c_str(), NPRT(dlerror()));
182          Dmsg2(dbglvl, "Lookup of unloadPlugin in plugin %s failed: ERR=%s\n",
183             fname.c_str(), NPRT(dlerror()));
184          close_plugin(plugin);
185          continue;
186       }
187
188       /* Initialize the plugin */
189       if (loadPlugin(binfo, bfuncs, &plugin->pinfo, &plugin->pfuncs) != bRC_OK) {
190          close_plugin(plugin);
191          continue;
192       }
193       if (!is_plugin_compatible) {
194          Dmsg0(50, "Plugin compatibility pointer not set.\n");   
195       } else if (!is_plugin_compatible(plugin)) {
196          close_plugin(plugin);
197          continue;
198       }
199
200       found = true;                /* found a plugin */
201       plugin_list->append(plugin);
202    }
203
204 get_out:
205    if (!found && plugin) {
206       close_plugin(plugin);
207    }
208    if (entry) {
209       free(entry);
210    }
211    if (dp) {
212       closedir(dp);
213    }
214    return found;
215 }
216
217 /*
218  * Unload all the loaded plugins 
219  */
220 void unload_plugins()
221 {
222    Plugin *plugin;
223
224    if (!plugin_list) {
225       return;
226    }
227    foreach_alist(plugin, plugin_list) {
228       /* Shut it down and unload it */
229       plugin->unloadPlugin();
230       dlclose(plugin->pHandle);
231       if (plugin->file) {
232          free(plugin->file);
233       }
234       free(plugin);
235    }
236    delete plugin_list;
237    plugin_list = NULL;
238 }
239
240 /*
241  * Dump plugin information
242  * Each daemon can register a hook that will be called
243  * after a fatal signal.
244  */
245 #define DBG_MAX_HOOK 10
246 static dbg_plugin_hook_t *dbg_plugin_hooks[DBG_MAX_HOOK];
247 static int dbg_plugin_hook_count=0;
248
249 void dbg_plugin_add_hook(dbg_plugin_hook_t *fct)
250 {
251    ASSERT(dbg_plugin_hook_count < DBG_MAX_HOOK);
252    dbg_plugin_hooks[dbg_plugin_hook_count++] = fct;
253 }
254
255 void dbg_print_plugin(FILE *fp)
256 {
257    Plugin *plugin;
258    fprintf(fp, "Attempt to dump plugins. Hook count=%d\n", dbg_plugin_hook_count);
259
260    if (!plugin_list) {
261       return;
262    }
263    foreach_alist(plugin, plugin_list) {
264       for(int i=0; i < dbg_plugin_hook_count; i++) {
265 //       dbg_plugin_hook_t *fct = dbg_plugin_hooks[i];
266          fprintf(fp, "Plugin %p name=\"%s\" disabled=%d\n",
267                  plugin, plugin->file, plugin->disabled);
268 //       fct(plugin, fp);
269       }
270    }
271 }