Home | History | Annotate | Download | only in src
      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