2 Bacula® - The Network Backup Solution
4 Copyright (C) 2007-2014 Free Software Foundation Europe e.V.
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.
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.
14 Bacula® is a registered trademark of Kern Sibbald.
17 * Plugin load/unloader for all Bacula daemons
19 * Kern Sibbald, October 2007
26 #define NAMELEN(dirent) (strlen((dirent)->d_name))
28 #ifndef HAVE_READDIR_R
29 int readdir_r(DIR *dirp, struct dirent *entry, struct dirent **result);
38 static const int dbglvl = 50;
41 * List of all loaded plugins.
43 * NOTE!!! This is a global do not try walking it with
44 * foreach_alist, you must use foreach_alist_index !!!!!!
46 alist *bplugin_list = NULL;
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.
57 plugin = (Plugin *)malloc(sizeof(Plugin));
58 memset(plugin, 0, sizeof(Plugin));
62 static void close_plugin(Plugin *plugin)
65 Dmsg1(50, "Got plugin=%s but not accepted.\n", plugin->file);
67 if (plugin->unloadPlugin) {
68 plugin->unloadPlugin();
70 if (plugin->pHandle) {
71 dlclose(plugin->pHandle);
80 * Load all the plugins in the specified directory.
82 bool load_plugins(void *binfo, void *bfuncs, const char *plugin_dir,
83 const char *type, bool is_plugin_compatible(Plugin *plugin))
86 t_loadPlugin loadPlugin;
87 Plugin *plugin = NULL;
89 struct dirent *entry = NULL, *result;
92 POOL_MEM fname(PM_FNAME);
93 bool need_slash = false;
97 Dmsg0(dbglvl, "load_plugins\n");
98 name_max = pathconf(".", _PC_NAME_MAX);
99 if (name_max < 1024) {
103 if (!(dp = opendir(plugin_dir))) {
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());
112 len = strlen(plugin_dir);
114 need_slash = !IsPathSeparator(plugin_dir[len - 1]);
116 entry = (struct dirent *)malloc(sizeof(struct dirent) + name_max + 1000);
118 plugin = NULL; /* Start from a fresh plugin */
120 if ((readdir_r(dp, entry, &result) != 0) || (result == NULL)) {
122 Jmsg(NULL, M_WARNING, 0, _("Failed to find any plugins in %s\n"),
124 Dmsg1(dbglvl, "Failed to find any plugins in %s\n", plugin_dir);
128 if (strcmp(result->d_name, ".") == 0 ||
129 strcmp(result->d_name, "..") == 0) {
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);
139 Dmsg2(dbglvl, "Found plugin: name=%s len=%d\n", result->d_name, len);
141 pm_strcpy(fname, plugin_dir);
143 pm_strcat(fname, "/");
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 */
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(),
160 close_plugin(plugin);
164 /* Get two global entry points */
165 loadPlugin = (t_loadPlugin)dlsym(plugin->pHandle, "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);
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);
184 /* Initialize the plugin */
185 if (loadPlugin(binfo, bfuncs, &plugin->pinfo, &plugin->pfuncs) != bRC_OK) {
186 close_plugin(plugin);
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);
196 found = true; /* found a plugin */
197 bplugin_list->append(plugin);
201 if (!found && plugin) {
202 close_plugin(plugin);
214 * Unload all the loaded plugins
216 void unload_plugins()
223 foreach_alist(plugin, bplugin_list) {
224 /* Shut it down and unload it */
225 plugin->unloadPlugin();
226 dlclose(plugin->pHandle);
237 * Dump plugin information
238 * Each daemon can register a hook that will be called
239 * after a fatal signal.
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;
245 void dbg_plugin_add_hook(dbg_plugin_hook_t *fct)
247 ASSERT(dbg_plugin_hook_count < DBG_MAX_HOOK);
248 dbg_plugin_hooks[dbg_plugin_hook_count++] = fct;
251 void dbg_print_plugin(FILE *fp)
254 fprintf(fp, "Attempt to dump plugins. Hook count=%d\n", dbg_plugin_hook_count);
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);