1 /* 2 * 3 * BlueZ - Bluetooth protocol stack for Linux 4 * 5 * Copyright (C) 2004-2010 Marcel Holtmann <marcel (at) holtmann.org> 6 * 7 * 8 * This program is free software; you can redistribute it and/or modify 9 * it under the terms of the GNU General Public License as published by 10 * the Free Software Foundation; either version 2 of the License, or 11 * (at your option) any later version. 12 * 13 * This program is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 * GNU 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 St, Fifth Floor, Boston, MA 02110-1301 USA 21 * 22 */ 23 24 #ifdef HAVE_CONFIG_H 25 #include <config.h> 26 #endif 27 28 #include <errno.h> 29 #include <dlfcn.h> 30 #include <string.h> 31 #include <sys/stat.h> 32 33 #include <bluetooth/bluetooth.h> 34 35 #include <glib.h> 36 37 #include "plugin.h" 38 #include "log.h" 39 #include "hcid.h" 40 #include "btio.h" 41 42 static GSList *plugins = NULL; 43 44 struct bluetooth_plugin { 45 void *handle; 46 gboolean active; 47 struct bluetooth_plugin_desc *desc; 48 }; 49 50 static gint compare_priority(gconstpointer a, gconstpointer b) 51 { 52 const struct bluetooth_plugin *plugin1 = a; 53 const struct bluetooth_plugin *plugin2 = b; 54 55 return plugin2->desc->priority - plugin1->desc->priority; 56 } 57 58 static gboolean add_plugin(void *handle, struct bluetooth_plugin_desc *desc) 59 { 60 struct bluetooth_plugin *plugin; 61 62 if (desc->init == NULL) 63 return FALSE; 64 65 if (g_str_equal(desc->version, VERSION) == FALSE) { 66 error("Version mismatch for %s", desc->name); 67 return FALSE; 68 } 69 70 DBG("Loading %s plugin", desc->name); 71 72 plugin = g_try_new0(struct bluetooth_plugin, 1); 73 if (plugin == NULL) 74 return FALSE; 75 76 plugin->handle = handle; 77 plugin->active = FALSE; 78 plugin->desc = desc; 79 80 plugins = g_slist_insert_sorted(plugins, plugin, compare_priority); 81 82 return TRUE; 83 } 84 85 static gboolean enable_plugin(const char *name, char **conf_disable, 86 char **cli_enable, char **cli_disable) 87 { 88 if (conf_disable) { 89 for (; *conf_disable; conf_disable++) 90 if (g_pattern_match_simple(*conf_disable, name)) 91 break; 92 if (*conf_disable) { 93 info("Excluding (conf) %s", name); 94 return FALSE; 95 } 96 } 97 98 if (cli_disable) { 99 for (; *cli_disable; cli_disable++) 100 if (g_pattern_match_simple(*cli_disable, name)) 101 break; 102 if (*cli_disable) { 103 info("Excluding (cli) %s", name); 104 return FALSE; 105 } 106 } 107 108 if (cli_enable) { 109 for (; *cli_enable; cli_enable++) 110 if (g_pattern_match_simple(*cli_enable, name)) 111 break; 112 if (!*cli_enable) { 113 info("Ignoring (cli) %s", name); 114 return FALSE; 115 } 116 } 117 118 return TRUE; 119 } 120 121 #include "builtin.h" 122 123 gboolean plugin_init(GKeyFile *config, const char *enable, const char *disable) 124 { 125 GSList *list; 126 GDir *dir; 127 const gchar *file; 128 char **conf_disabled, **cli_disabled, **cli_enabled; 129 unsigned int i; 130 131 /* Make a call to BtIO API so its symbols got resolved before the 132 * plugins are loaded. */ 133 bt_io_error_quark(); 134 135 if (config) 136 conf_disabled = g_key_file_get_string_list(config, "General", 137 "DisablePlugins", 138 NULL, NULL); 139 else 140 conf_disabled = NULL; 141 142 if (enable) 143 cli_enabled = g_strsplit_set(enable, ", ", -1); 144 else 145 cli_enabled = NULL; 146 147 if (disable) 148 cli_disabled = g_strsplit_set(disable, ", ", -1); 149 else 150 cli_disabled = NULL; 151 152 DBG("Loading builtin plugins"); 153 154 for (i = 0; __bluetooth_builtin[i]; i++) { 155 if (!enable_plugin(__bluetooth_builtin[i]->name, conf_disabled, 156 cli_enabled, cli_disabled)) 157 continue; 158 159 add_plugin(NULL, __bluetooth_builtin[i]); 160 } 161 162 if (strlen(PLUGINDIR) == 0) 163 goto start; 164 165 DBG("Loading plugins %s", PLUGINDIR); 166 167 dir = g_dir_open(PLUGINDIR, 0, NULL); 168 if (!dir) 169 goto start; 170 171 while ((file = g_dir_read_name(dir)) != NULL) { 172 struct bluetooth_plugin_desc *desc; 173 void *handle; 174 gchar *filename; 175 176 if (g_str_has_prefix(file, "lib") == TRUE || 177 g_str_has_suffix(file, ".so") == FALSE) 178 continue; 179 180 filename = g_build_filename(PLUGINDIR, file, NULL); 181 182 handle = dlopen(filename, RTLD_NOW); 183 if (handle == NULL) { 184 error("Can't load plugin %s: %s", filename, 185 dlerror()); 186 g_free(filename); 187 continue; 188 } 189 190 g_free(filename); 191 192 desc = dlsym(handle, "bluetooth_plugin_desc"); 193 if (desc == NULL) { 194 error("Can't load plugin description: %s", dlerror()); 195 dlclose(handle); 196 continue; 197 } 198 199 if (!enable_plugin(desc->name, conf_disabled, 200 cli_enabled, cli_disabled)) { 201 dlclose(handle); 202 continue; 203 } 204 205 if (add_plugin(handle, desc) == FALSE) 206 dlclose(handle); 207 } 208 209 g_dir_close(dir); 210 211 start: 212 for (list = plugins; list; list = list->next) { 213 struct bluetooth_plugin *plugin = list->data; 214 215 if (plugin->desc->init() < 0) { 216 error("Failed to init %s plugin", plugin->desc->name); 217 continue; 218 } 219 220 plugin->active = TRUE; 221 } 222 223 g_strfreev(conf_disabled); 224 g_strfreev(cli_enabled); 225 g_strfreev(cli_disabled); 226 227 return TRUE; 228 } 229 230 void plugin_cleanup(void) 231 { 232 GSList *list; 233 234 DBG("Cleanup plugins"); 235 236 for (list = plugins; list; list = list->next) { 237 struct bluetooth_plugin *plugin = list->data; 238 239 if (plugin->active == TRUE && plugin->desc->exit) 240 plugin->desc->exit(); 241 242 if (plugin->handle != NULL) 243 dlclose(plugin->handle); 244 245 g_free(plugin); 246 } 247 248 g_slist_free(plugins); 249 } 250