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