2 Bacula(R) - The Network Backup Solution
4 Copyright (C) 2000-2016 Kern Sibbald
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.
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 This notice must be preserved when any source code is
15 conveyed and/or propagated.
17 Bacula(R) is a registered trademark of Kern Sibbald.
20 * Plugin load/unloader for all Bacula daemons
22 * Kern Sibbald, October 2007
29 #define NAMELEN(dirent) (strlen((dirent)->d_name))
31 #ifndef HAVE_READDIR_R
32 int readdir_r(DIR *dirp, struct dirent *entry, struct dirent **result);
41 static const int dbglvl = 50;
44 * List of all loaded plugins.
46 * NOTE!!! This is a global do not try walking it with
47 * foreach_alist, you must use foreach_alist_index !!!!!!
49 alist *b_plugin_list = NULL;
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.
60 plugin = (Plugin *)malloc(sizeof(Plugin));
61 memset(plugin, 0, sizeof(Plugin));
65 static void close_plugin(Plugin *plugin)
68 Dmsg1(50, "Got plugin=%s but not accepted.\n", plugin->file);
70 if (plugin->unloadPlugin) {
71 plugin->unloadPlugin();
73 if (plugin->pHandle) {
74 dlclose(plugin->pHandle);
83 * Load all the plugins in the specified directory.
85 bool load_plugins(void *binfo, void *bfuncs, const char *plugin_dir,
86 const char *type, bool is_plugin_compatible(Plugin *plugin))
89 t_loadPlugin loadPlugin;
90 Plugin *plugin = NULL;
92 struct dirent *entry = NULL, *result;
95 POOL_MEM fname(PM_FNAME);
96 bool need_slash = false;
100 Dmsg0(dbglvl, "load_plugins\n");
101 name_max = pathconf(".", _PC_NAME_MAX);
102 if (name_max < 1024) {
106 if (!(dp = opendir(plugin_dir))) {
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());
115 len = strlen(plugin_dir);
117 need_slash = !IsPathSeparator(plugin_dir[len - 1]);
119 entry = (struct dirent *)malloc(sizeof(struct dirent) + name_max + 1000);
121 plugin = NULL; /* Start from a fresh plugin */
123 if ((readdir_r(dp, entry, &result) != 0) || (result == NULL)) {
125 Jmsg(NULL, M_WARNING, 0, _("Failed to find any plugins in %s\n"),
127 Dmsg1(dbglvl, "Failed to find any plugins in %s\n", plugin_dir);
131 if (strcmp(result->d_name, ".") == 0 ||
132 strcmp(result->d_name, "..") == 0) {
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);
142 Dmsg2(dbglvl, "Found plugin: name=%s len=%d\n", result->d_name, len);
144 pm_strcpy(fname, plugin_dir);
146 pm_strcat(fname, "/");
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 */
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(),
163 close_plugin(plugin);
167 /* Get two global entry points */
168 loadPlugin = (t_loadPlugin)dlsym(plugin->pHandle, "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);
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);
187 /* Initialize the plugin */
188 if (loadPlugin(binfo, bfuncs, &plugin->pinfo, &plugin->pfuncs) != bRC_OK) {
189 close_plugin(plugin);
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);
199 found = true; /* found a plugin */
200 b_plugin_list->append(plugin);
204 if (!found && plugin) {
205 close_plugin(plugin);
217 * Unload all the loaded plugins
219 void unload_plugins()
223 if (!b_plugin_list) {
226 foreach_alist(plugin, b_plugin_list) {
227 /* Shut it down and unload it */
228 plugin->unloadPlugin();
229 dlclose(plugin->pHandle);
235 delete b_plugin_list;
236 b_plugin_list = NULL;
240 * Dump plugin information
241 * Each daemon can register a hook that will be called
242 * after a fatal signal.
244 #define DBG_MAX_HOOK 10
245 static dbg_plugin_hook_t *dbg_plugin_hooks[DBG_MAX_HOOK];
246 static int dbg_plugin_hook_count=0;
248 void dbg_plugin_add_hook(dbg_plugin_hook_t *fct)
250 ASSERT(dbg_plugin_hook_count < DBG_MAX_HOOK);
251 dbg_plugin_hooks[dbg_plugin_hook_count++] = fct;
254 void dbg_print_plugin(FILE *fp)
257 fprintf(fp, "List plugins. Hook count=%d\n", dbg_plugin_hook_count);
259 if (!b_plugin_list) {
262 foreach_alist(plugin, b_plugin_list) {
263 for(int i=0; i < dbg_plugin_hook_count; i++) {
264 // dbg_plugin_hook_t *fct = dbg_plugin_hooks[i];
265 fprintf(fp, "Plugin %p name=\"%s\" disabled=%d\n",
266 plugin, plugin->file, plugin->disabled);