2 Bacula® - The Network Backup Solution
4 Copyright (C) 2007-2009 Free Software Foundation Europe e.V.
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.
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.
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
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.
29 * Plugin load/unloader for all Bacula daemons
31 * Kern Sibbald, October 2007
38 #define NAMELEN(dirent) (strlen((dirent)->d_name))
40 #ifndef HAVE_READDIR_R
41 int readdir_r(DIR *dirp, struct dirent *entry, struct dirent **result);
50 static const int dbglvl = 50;
52 /* All loaded plugins */
53 alist *plugin_list = NULL;
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.
64 plugin = (Plugin *)malloc(sizeof(Plugin));
65 memset(plugin, 0, sizeof(Plugin));
69 static void close_plugin(Plugin *plugin)
72 Dmsg1(50, "Got plugin=%s but not accepted.\n", plugin->file);
74 if (plugin->unloadPlugin) {
75 plugin->unloadPlugin();
77 if (plugin->pHandle) {
78 dlclose(plugin->pHandle);
87 * Load all the plugins in the specified directory.
89 bool load_plugins(void *binfo, void *bfuncs, const char *plugin_dir,
90 const char *type, bool is_plugin_compatible(Plugin *plugin))
93 t_loadPlugin loadPlugin;
94 Plugin *plugin = NULL;
96 struct dirent *entry = NULL, *result;
99 POOL_MEM fname(PM_FNAME);
100 bool need_slash = false;
104 name_max = pathconf(".", _PC_NAME_MAX);
105 if (name_max < 1024) {
109 if (!(dp = opendir(plugin_dir))) {
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());
118 len = strlen(plugin_dir);
120 need_slash = !IsPathSeparator(plugin_dir[len - 1]);
122 entry = (struct dirent *)malloc(sizeof(struct dirent) + name_max + 1000);
124 if ((readdir_r(dp, entry, &result) != 0) || (result == NULL)) {
126 Jmsg(NULL, M_WARNING, 0, _("Failed to find any plugins in %s\n"),
128 Dmsg1(dbglvl, "Failed to find any plugins in %s\n", plugin_dir);
132 if (strcmp(result->d_name, ".") == 0 ||
133 strcmp(result->d_name, "..") == 0) {
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);
143 Dmsg2(dbglvl, "Loaded plugin: name=%s len=%d\n", result->d_name, len);
145 pm_strcpy(fname, plugin_dir);
147 pm_strcat(fname, "/");
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 */
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(),
162 close_plugin(plugin);
166 /* Get two global entry points */
167 loadPlugin = (t_loadPlugin)dlsym(plugin->pHandle, "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);
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);
186 /* Initialize the plugin */
187 if (loadPlugin(binfo, bfuncs, &plugin->pinfo, &plugin->pfuncs) != bRC_OK) {
188 close_plugin(plugin);
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);
198 found = true; /* found a plugin */
199 plugin_list->append(plugin);
203 if (!found && plugin) {
204 close_plugin(plugin);
216 * Unload all the loaded plugins
218 void unload_plugins()
225 foreach_alist(plugin, plugin_list) {
226 /* Shut it down and unload it */
227 plugin->unloadPlugin();
228 dlclose(plugin->pHandle);
239 * Dump plugin information
240 * Each daemon can register a hook that will be called
241 * after a fatal signal.
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;
247 void dbg_plugin_add_hook(dbg_plugin_hook_t *fct)
249 ASSERT(dbg_plugin_hook_count < DBG_MAX_HOOK);
250 dbg_plugin_hooks[dbg_plugin_hook_count++] = fct;
253 void dbg_print_plugin(FILE *fp)
256 fprintf(fp, "Attempt to dump plugins. Hook count=%d\n", dbg_plugin_hook_count);
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);