]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/lib/plugins.c
Backport from BEE
[bacula/bacula] / bacula / src / lib / plugins.c
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2007-2014 Free Software Foundation Europe e.V.
5
6    The main author of Bacula is Kern Sibbald, with contributions from many
7    others, a complete list can be found in the file AUTHORS.
8
9    You may use this file and others of this release according to the
10    license defined in the LICENSE file, which includes the Affero General
11    Public License, v3.0 ("AGPLv3") and some additional permissions and
12    terms pursuant to its AGPLv3 Section 7.
13
14    Bacula® is a registered trademark of Kern Sibbald.
15 */
16 /*
17  *    Plugin load/unloader for all Bacula daemons
18  *
19  * Kern Sibbald, October 2007
20  */
21
22 #include "bacula.h"
23 #include <dlfcn.h>
24 #ifdef HAVE_DIRENT_H
25 #include <dirent.h>
26 #define NAMELEN(dirent) (strlen((dirent)->d_name))
27 #endif
28 #ifndef HAVE_READDIR_R
29 int readdir_r(DIR *dirp, struct dirent *entry, struct dirent **result);
30 #endif
31
32 #ifndef RTLD_NOW
33 #define RTLD_NOW 2
34 #endif
35
36 #include "plugins.h"
37
38 static const int dbglvl = 50;
39
40 /*
41  * List of all loaded plugins.
42  *
43  * NOTE!!! This is a global do not try walking it with
44  *   foreach_alist, you must use foreach_alist_index !!!!!!
45  */
46 alist *bplugin_list = NULL;
47
48 /*
49  * Create a new plugin "class" entry and enter it in the
50  *  list of plugins.  Note, this is not the same as
51  *  an instance of the plugin.
52  */
53 Plugin *new_plugin()
54 {
55    Plugin *plugin;
56
57    plugin = (Plugin *)malloc(sizeof(Plugin));
58    memset(plugin, 0, sizeof(Plugin));
59    return plugin;
60 }
61
62 static void close_plugin(Plugin *plugin)
63 {
64    if (plugin->file) {
65       Dmsg1(50, "Got plugin=%s but not accepted.\n", plugin->file);
66    }
67    if (plugin->unloadPlugin) {
68       plugin->unloadPlugin();
69    }
70    if (plugin->pHandle) {
71       dlclose(plugin->pHandle);
72    }
73    if (plugin->file) {
74       free(plugin->file);
75    }
76    free(plugin);
77 }
78
79 /*
80  * Load all the plugins in the specified directory.
81  */
82 bool load_plugins(void *binfo, void *bfuncs, const char *plugin_dir,
83         const char *type, bool is_plugin_compatible(Plugin *plugin))
84 {
85    bool found = false;
86    t_loadPlugin loadPlugin;
87    Plugin *plugin = NULL;
88    DIR* dp = NULL;
89    struct dirent *entry = NULL, *result;
90    int name_max;
91    struct stat statp;
92    POOL_MEM fname(PM_FNAME);
93    bool need_slash = false;
94    int len, type_len;
95
96
97    Dmsg0(dbglvl, "load_plugins\n");
98    name_max = pathconf(".", _PC_NAME_MAX);
99    if (name_max < 1024) {
100       name_max = 1024;
101    }
102
103    if (!(dp = opendir(plugin_dir))) {
104       berrno be;
105       Jmsg(NULL, M_ERROR_TERM, 0, _("Failed to open Plugin directory %s: ERR=%s\n"),
106             plugin_dir, be.bstrerror());
107       Dmsg2(dbglvl, "Failed to open Plugin directory %s: ERR=%s\n",
108             plugin_dir, be.bstrerror());
109       goto get_out;
110    }
111
112    len = strlen(plugin_dir);
113    if (len > 0) {
114       need_slash = !IsPathSeparator(plugin_dir[len - 1]);
115    }
116    entry = (struct dirent *)malloc(sizeof(struct dirent) + name_max + 1000);
117    for ( ;; ) {
118       plugin = NULL;            /* Start from a fresh plugin */
119
120       if ((readdir_r(dp, entry, &result) != 0) || (result == NULL)) {
121          if (!found) {
122             Jmsg(NULL, M_WARNING, 0, _("Failed to find any plugins in %s\n"),
123                   plugin_dir);
124             Dmsg1(dbglvl, "Failed to find any plugins in %s\n", plugin_dir);
125          }
126          break;
127       }
128       if (strcmp(result->d_name, ".") == 0 ||
129           strcmp(result->d_name, "..") == 0) {
130          continue;
131       }
132
133       len = strlen(result->d_name);
134       type_len = strlen(type);
135       if (len < type_len+1 || strcmp(&result->d_name[len-type_len], type) != 0) {
136          Dmsg3(dbglvl, "Rejected plugin: want=%s name=%s len=%d\n", type, result->d_name, len);
137          continue;
138       }
139       Dmsg2(dbglvl, "Found plugin: name=%s len=%d\n", result->d_name, len);
140
141       pm_strcpy(fname, plugin_dir);
142       if (need_slash) {
143          pm_strcat(fname, "/");
144       }
145       pm_strcat(fname, result->d_name);
146       if (lstat(fname.c_str(), &statp) != 0 || !S_ISREG(statp.st_mode)) {
147          continue;                 /* ignore directories & special files */
148       }
149
150       plugin = new_plugin();
151       plugin->file = bstrdup(result->d_name);
152       plugin->file_len = strstr(plugin->file, type) - plugin->file;
153       plugin->pHandle = dlopen(fname.c_str(), RTLD_NOW);
154       if (!plugin->pHandle) {
155          const char *error = dlerror();
156          Jmsg(NULL, M_ERROR, 0, _("dlopen plugin %s failed: ERR=%s\n"),
157               fname.c_str(), NPRT(error));
158          Dmsg2(dbglvl, "dlopen plugin %s failed: ERR=%s\n", fname.c_str(),
159                NPRT(error));
160          close_plugin(plugin);
161          continue;
162       }
163
164       /* Get two global entry points */
165       loadPlugin = (t_loadPlugin)dlsym(plugin->pHandle, "loadPlugin");
166       if (!loadPlugin) {
167          Jmsg(NULL, M_ERROR, 0, _("Lookup of loadPlugin in plugin %s failed: ERR=%s\n"),
168             fname.c_str(), NPRT(dlerror()));
169          Dmsg2(dbglvl, "Lookup of loadPlugin in plugin %s failed: ERR=%s\n",
170             fname.c_str(), NPRT(dlerror()));
171          close_plugin(plugin);
172          continue;
173       }
174       plugin->unloadPlugin = (t_unloadPlugin)dlsym(plugin->pHandle, "unloadPlugin");
175       if (!plugin->unloadPlugin) {
176          Jmsg(NULL, M_ERROR, 0, _("Lookup of unloadPlugin in plugin %s failed: ERR=%s\n"),
177             fname.c_str(), NPRT(dlerror()));
178          Dmsg2(dbglvl, "Lookup of unloadPlugin in plugin %s failed: ERR=%s\n",
179             fname.c_str(), NPRT(dlerror()));
180          close_plugin(plugin);
181          continue;
182       }
183
184       /* Initialize the plugin */
185       if (loadPlugin(binfo, bfuncs, &plugin->pinfo, &plugin->pfuncs) != bRC_OK) {
186          close_plugin(plugin);
187          continue;
188       }
189       if (!is_plugin_compatible) {
190          Dmsg0(50, "Plugin compatibility pointer not set.\n");
191       } else if (!is_plugin_compatible(plugin)) {
192          close_plugin(plugin);
193          continue;
194       }
195
196       found = true;                /* found a plugin */
197       bplugin_list->append(plugin);
198    }
199
200 get_out:
201    if (!found && plugin) {
202       close_plugin(plugin);
203    }
204    if (entry) {
205       free(entry);
206    }
207    if (dp) {
208       closedir(dp);
209    }
210    return found;
211 }
212
213 /*
214  * Unload all the loaded plugins
215  */
216 void unload_plugins()
217 {
218    Plugin *plugin;
219
220    if (!bplugin_list) {
221       return;
222    }
223    foreach_alist(plugin, bplugin_list) {
224       /* Shut it down and unload it */
225       plugin->unloadPlugin();
226       dlclose(plugin->pHandle);
227       if (plugin->file) {
228          free(plugin->file);
229       }
230       free(plugin);
231    }
232    delete bplugin_list;
233    bplugin_list = NULL;
234 }
235
236 /*
237  * Dump plugin information
238  * Each daemon can register a hook that will be called
239  * after a fatal signal.
240  */
241 #define DBG_MAX_HOOK 10
242 static dbg_plugin_hook_t *dbg_plugin_hooks[DBG_MAX_HOOK];
243 static int dbg_plugin_hook_count=0;
244
245 void dbg_plugin_add_hook(dbg_plugin_hook_t *fct)
246 {
247    ASSERT(dbg_plugin_hook_count < DBG_MAX_HOOK);
248    dbg_plugin_hooks[dbg_plugin_hook_count++] = fct;
249 }
250
251 void dbg_print_plugin(FILE *fp)
252 {
253    Plugin *plugin;
254    fprintf(fp, "Attempt to dump plugins. Hook count=%d\n", dbg_plugin_hook_count);
255
256    if (!bplugin_list) {
257       return;
258    }
259    foreach_alist(plugin, bplugin_list) {
260       for(int i=0; i < dbg_plugin_hook_count; i++) {
261 //       dbg_plugin_hook_t *fct = dbg_plugin_hooks[i];
262          fprintf(fp, "Plugin %p name=\"%s\" disabled=%d\n",
263                  plugin, plugin->file, plugin->disabled);
264 //       fct(plugin, fp);
265       }
266    }
267 }