1 /*
2 *
3 * BlueZ - Bluetooth protocol stack for Linux
4 *
5 * Copyright (C) 2004-2010 Marcel Holtmann <marcel@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
compare_priority(gconstpointer a,gconstpointer b)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
add_plugin(void * handle,struct bluetooth_plugin_desc * desc)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
enable_plugin(const char * name,char ** conf_disable,char ** cli_enable,char ** cli_disable)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
plugin_init(GKeyFile * config,const char * enable,const char * disable)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
plugin_cleanup(void)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