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