]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/lib/plugins.c
6cc5e43286b855fcba3468485f7c891efce698d1
[bacula/bacula] / bacula / src / lib / plugins.c
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2007-2008 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, const char *type)
74 {
75    bool found = false;
76    t_loadPlugin loadPlugin;
77    Plugin *plugin = NULL;
78    DIR* dp = NULL;
79    struct dirent *entry = NULL, *result;
80    int name_max;
81    struct stat statp;
82    POOL_MEM fname(PM_FNAME);
83    bool need_slash = false;
84    int len, type_len;
85
86
87    name_max = pathconf(".", _PC_NAME_MAX);
88    if (name_max < 1024) {
89       name_max = 1024;
90    }
91
92    if (!(dp = opendir(plugin_dir))) {
93       berrno be;
94       Jmsg(NULL, M_ERROR_TERM, 0, _("Failed to open Plugin directory %s: ERR=%s\n"), 
95             plugin_dir, be.bstrerror());
96       Dmsg2(dbglvl, "Failed to open Plugin directory %s: ERR=%s\n", 
97             plugin_dir, be.bstrerror());
98       goto get_out;
99    }
100    
101    len = strlen(plugin_dir);
102    if (len > 0) {
103       need_slash = !IsPathSeparator(plugin_dir[len - 1]);
104    }
105    entry = (struct dirent *)malloc(sizeof(struct dirent) + name_max + 1000);
106    for ( ;; ) {
107       if ((readdir_r(dp, entry, &result) != 0) || (result == NULL)) {
108          if (!found) {
109             Jmsg(NULL, M_WARNING, 0, _("Failed to find any plugins in %s\n"), 
110                   plugin_dir);
111             Dmsg1(dbglvl, "Failed to find any plugins in %s\n", plugin_dir);
112          }
113          break;
114       }
115       if (strcmp(result->d_name, ".") == 0 || 
116           strcmp(result->d_name, "..") == 0) {
117          continue;
118       }
119
120       len = strlen(result->d_name);
121       type_len = strlen(type);
122       if (len < type_len+1 || strcmp(&result->d_name[len-type_len], type) != 0) {
123          Dmsg3(dbglvl, "Rejected plugin: want=%s name=%s len=%d\n", type, result->d_name, len);
124          continue;
125       }
126       Dmsg2(dbglvl, "Loaded plugin: name=%s len=%d\n", result->d_name, len);
127        
128       pm_strcpy(fname, plugin_dir);
129       if (need_slash) {
130          pm_strcat(fname, "/");
131       }
132       pm_strcat(fname, result->d_name);
133       if (lstat(fname.c_str(), &statp) != 0 || !S_ISREG(statp.st_mode)) {
134          continue;                 /* ignore directories & special files */
135       }
136
137       plugin = new_plugin();
138       plugin->file = bstrdup(result->d_name);
139       plugin->pHandle = dlopen(fname.c_str(), RTLD_NOW);
140       if (!plugin->pHandle) {
141          Jmsg(NULL, M_ERROR, 0, _("Plugin load %s failed: ERR=%s\n"), 
142               fname.c_str(), NPRT(dlerror()));
143          Dmsg2(dbglvl, "Plugin load %s failed: ERR=%s\n", fname.c_str(), 
144                NPRT(dlerror()));
145          goto get_out;
146       }
147
148       /* Get two global entry points */
149       loadPlugin = (t_loadPlugin)dlsym(plugin->pHandle, "loadPlugin");
150       if (!loadPlugin) {
151          Jmsg(NULL, M_ERROR, 0, _("Lookup of loadPlugin in plugin %s failed: ERR=%s\n"),
152             fname.c_str(), NPRT(dlerror()));
153          Dmsg2(dbglvl, "Lookup of loadPlugin in plugin %s failed: ERR=%s\n", 
154             fname.c_str(), NPRT(dlerror()));
155          goto get_out;
156       }
157       plugin->unloadPlugin = (t_unloadPlugin)dlsym(plugin->pHandle, "unloadPlugin");
158       if (!plugin->unloadPlugin) {
159          Jmsg(NULL, M_ERROR, 0, _("Lookup of unloadPlugin in plugin %s failed: ERR=%s\n"),
160             fname.c_str(), NPRT(dlerror()));
161          Dmsg2(dbglvl, "Lookup of unloadPlugin in plugin %s failed: ERR=%s\n",
162             fname.c_str(), NPRT(dlerror()));
163          goto get_out;
164       }
165
166       /* Initialize the plugin */
167       if (loadPlugin(binfo, bfuncs, &plugin->pinfo, &plugin->pfuncs) != bRC_OK) {
168          goto get_out;
169       }
170
171       found = true;                /* found a plugin */
172       plugin_list->append(plugin);
173    }
174
175 get_out:
176    if (!found && plugin) {
177       free(plugin);
178    }
179    if (entry) {
180       free(entry);
181    }
182    if (dp) {
183       closedir(dp);
184    }
185    return found;
186 }
187
188 /*
189  * Unload all the loaded plugins 
190  */
191 void unload_plugins()
192 {
193    Plugin *plugin;
194
195    if (!plugin_list) {
196       return;
197    }
198    foreach_alist(plugin, plugin_list) {
199       /* Shut it down and unload it */
200       plugin->unloadPlugin();
201       dlclose(plugin->pHandle);
202       if (plugin->file) {
203          free(plugin->file);
204       }
205       free(plugin);
206    }
207    delete plugin_list;
208    plugin_list = NULL;
209 }