]> code.delx.au - pulseaudio/blob - src/pulsecore/module.c
module: Create pa_module_exists()
[pulseaudio] / src / pulsecore / module.c
1 /***
2 This file is part of PulseAudio.
3
4 Copyright 2004-2006 Lennart Poettering
5 Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
6
7 PulseAudio is free software; you can redistribute it and/or modify
8 it under the terms of the GNU Lesser General Public License as published
9 by the Free Software Foundation; either version 2.1 of the License,
10 or (at your option) any later version.
11
12 PulseAudio is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public License
18 along with PulseAudio; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
20 USA.
21 ***/
22
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <errno.h>
31 #include <ltdl.h>
32
33 #include <pulse/xmalloc.h>
34 #include <pulse/proplist.h>
35
36 #include <pulsecore/core-subscribe.h>
37 #include <pulsecore/log.h>
38 #include <pulsecore/core-util.h>
39 #include <pulsecore/macro.h>
40 #include <pulsecore/ltdl-helper.h>
41 #include <pulsecore/modinfo.h>
42
43 #include "module.h"
44
45 #define PA_SYMBOL_INIT "pa__init"
46 #define PA_SYMBOL_DONE "pa__done"
47 #define PA_SYMBOL_LOAD_ONCE "pa__load_once"
48 #define PA_SYMBOL_GET_N_USED "pa__get_n_used"
49 #define PA_SYMBOL_GET_DEPRECATE "pa__get_deprecated"
50
51 bool pa_module_exists(const char *name) {
52 const char *paths, *state = NULL;
53 char *n, *p, *pathname;
54 bool result;
55
56 pa_assert(name);
57
58 if (name[0] == PA_PATH_SEP_CHAR) {
59 result = access(name, F_OK) == 0 ? true : false;
60 pa_log_debug("Checking for existence of '%s': %s", name, result ? "success" : "failure");
61 if (result)
62 return true;
63 }
64
65 if (!(paths = lt_dlgetsearchpath()))
66 return false;
67
68 /* strip .so from the end of name, if present */
69 n = pa_xstrdup(name);
70 p = rindex(n, '.');
71 if (p && pa_streq(p, PA_SOEXT))
72 p[0] = 0;
73
74 while ((p = pa_split(paths, ":", &state))) {
75 pathname = pa_sprintf_malloc("%s" PA_PATH_SEP "%s" PA_SOEXT, p, n);
76 result = access(pathname, F_OK) == 0 ? true : false;
77 pa_log_debug("Checking for existence of '%s': %s", pathname, result ? "success" : "failure");
78 pa_xfree(pathname);
79 pa_xfree(p);
80 if (result) {
81 pa_xfree(n);
82 return true;
83 }
84 }
85
86 state = NULL;
87 if (PA_UNLIKELY(pa_run_from_build_tree())) {
88 while ((p = pa_split(paths, ":", &state))) {
89 pathname = pa_sprintf_malloc("%s" PA_PATH_SEP ".libs" PA_PATH_SEP "%s" PA_SOEXT, p, n);
90 result = access(pathname, F_OK) == 0 ? true : false;
91 pa_log_debug("Checking for existence of '%s': %s", pathname, result ? "success" : "failure");
92 pa_xfree(pathname);
93 pa_xfree(p);
94 if (result) {
95 pa_xfree(n);
96 return true;
97 }
98 }
99 }
100
101 pa_xfree(n);
102 return false;
103 }
104
105 pa_module* pa_module_load(pa_core *c, const char *name, const char *argument) {
106 pa_module *m = NULL;
107 bool (*load_once)(void);
108 const char* (*get_deprecated)(void);
109 pa_modinfo *mi;
110
111 pa_assert(c);
112 pa_assert(name);
113
114 if (c->disallow_module_loading)
115 goto fail;
116
117 m = pa_xnew(pa_module, 1);
118 m->name = pa_xstrdup(name);
119 m->argument = pa_xstrdup(argument);
120 m->load_once = false;
121 m->proplist = pa_proplist_new();
122 m->index = PA_IDXSET_INVALID;
123
124 if (!(m->dl = lt_dlopenext(name))) {
125 /* We used to print the error that is returned by lt_dlerror(), but
126 * lt_dlerror() is useless. It returns pretty much always "file not
127 * found". That's because if there are any problems with loading the
128 * module with normal loaders, libltdl falls back to the "preload"
129 * loader, which never finds anything, and therefore says "file not
130 * found". */
131 pa_log("Failed to open module \"%s\".", name);
132 goto fail;
133 }
134
135 if ((load_once = (bool (*)(void)) pa_load_sym(m->dl, name, PA_SYMBOL_LOAD_ONCE))) {
136
137 m->load_once = load_once();
138
139 if (m->load_once) {
140 pa_module *i;
141 uint32_t idx;
142 /* OK, the module only wants to be loaded once, let's make sure it is */
143
144 PA_IDXSET_FOREACH(i, c->modules, idx) {
145 if (pa_streq(name, i->name)) {
146 pa_log("Module \"%s\" should be loaded once at most. Refusing to load.", name);
147 goto fail;
148 }
149 }
150 }
151 }
152
153 if ((get_deprecated = (const char* (*) (void)) pa_load_sym(m->dl, name, PA_SYMBOL_GET_DEPRECATE))) {
154 const char *t;
155
156 if ((t = get_deprecated()))
157 pa_log_warn("%s is deprecated: %s", name, t);
158 }
159
160 if (!(m->init = (int (*)(pa_module*_m)) pa_load_sym(m->dl, name, PA_SYMBOL_INIT))) {
161 pa_log("Failed to load module \"%s\": symbol \""PA_SYMBOL_INIT"\" not found.", name);
162 goto fail;
163 }
164
165 m->done = (void (*)(pa_module*_m)) pa_load_sym(m->dl, name, PA_SYMBOL_DONE);
166 m->get_n_used = (int (*)(pa_module*_m)) pa_load_sym(m->dl, name, PA_SYMBOL_GET_N_USED);
167 m->userdata = NULL;
168 m->core = c;
169 m->unload_requested = false;
170
171 pa_assert_se(pa_idxset_put(c->modules, m, &m->index) >= 0);
172 pa_assert(m->index != PA_IDXSET_INVALID);
173
174 if (m->init(m) < 0) {
175 pa_log_error("Failed to load module \"%s\" (argument: \"%s\"): initialization failed.", name, argument ? argument : "");
176 goto fail;
177 }
178
179 pa_log_info("Loaded \"%s\" (index: #%u; argument: \"%s\").", m->name, m->index, m->argument ? m->argument : "");
180
181 pa_subscription_post(c, PA_SUBSCRIPTION_EVENT_MODULE|PA_SUBSCRIPTION_EVENT_NEW, m->index);
182
183 if ((mi = pa_modinfo_get_by_handle(m->dl, name))) {
184
185 if (mi->author && !pa_proplist_contains(m->proplist, PA_PROP_MODULE_AUTHOR))
186 pa_proplist_sets(m->proplist, PA_PROP_MODULE_AUTHOR, mi->author);
187
188 if (mi->description && !pa_proplist_contains(m->proplist, PA_PROP_MODULE_DESCRIPTION))
189 pa_proplist_sets(m->proplist, PA_PROP_MODULE_DESCRIPTION, mi->description);
190
191 if (mi->version && !pa_proplist_contains(m->proplist, PA_PROP_MODULE_VERSION))
192 pa_proplist_sets(m->proplist, PA_PROP_MODULE_VERSION, mi->version);
193
194 pa_modinfo_free(mi);
195 }
196
197 return m;
198
199 fail:
200
201 if (m) {
202 if (m->index != PA_IDXSET_INVALID)
203 pa_idxset_remove_by_index(c->modules, m->index);
204
205 if (m->proplist)
206 pa_proplist_free(m->proplist);
207
208 pa_xfree(m->argument);
209 pa_xfree(m->name);
210
211 if (m->dl)
212 lt_dlclose(m->dl);
213
214 pa_xfree(m);
215 }
216
217 return NULL;
218 }
219
220 static void pa_module_free(pa_module *m) {
221 pa_assert(m);
222 pa_assert(m->core);
223
224 pa_log_info("Unloading \"%s\" (index: #%u).", m->name, m->index);
225
226 if (m->done)
227 m->done(m);
228
229 if (m->proplist)
230 pa_proplist_free(m->proplist);
231
232 lt_dlclose(m->dl);
233
234 pa_log_info("Unloaded \"%s\" (index: #%u).", m->name, m->index);
235
236 pa_subscription_post(m->core, PA_SUBSCRIPTION_EVENT_MODULE|PA_SUBSCRIPTION_EVENT_REMOVE, m->index);
237
238 pa_xfree(m->name);
239 pa_xfree(m->argument);
240 pa_xfree(m);
241 }
242
243 void pa_module_unload(pa_core *c, pa_module *m, bool force) {
244 pa_assert(c);
245 pa_assert(m);
246
247 if (m->core->disallow_module_loading && !force)
248 return;
249
250 if (!(m = pa_idxset_remove_by_data(c->modules, m, NULL)))
251 return;
252
253 pa_module_free(m);
254 }
255
256 void pa_module_unload_by_index(pa_core *c, uint32_t idx, bool force) {
257 pa_module *m;
258 pa_assert(c);
259 pa_assert(idx != PA_IDXSET_INVALID);
260
261 if (c->disallow_module_loading && !force)
262 return;
263
264 if (!(m = pa_idxset_remove_by_index(c->modules, idx)))
265 return;
266
267 pa_module_free(m);
268 }
269
270 void pa_module_unload_all(pa_core *c) {
271 pa_module *m;
272 uint32_t *indices;
273 uint32_t state;
274 int i;
275
276 pa_assert(c);
277 pa_assert(c->modules);
278
279 if (pa_idxset_isempty(c->modules))
280 return;
281
282 /* Unload modules in reverse order by default */
283 indices = pa_xnew(uint32_t, pa_idxset_size(c->modules));
284 i = 0;
285 PA_IDXSET_FOREACH(m, c->modules, state)
286 indices[i++] = state;
287 pa_assert(i == (int) pa_idxset_size(c->modules));
288 i--;
289 for (; i >= 0; i--) {
290 m = pa_idxset_remove_by_index(c->modules, indices[i]);
291 if (m)
292 pa_module_free(m);
293 }
294 pa_xfree(indices);
295
296 /* Just in case module unloading caused more modules to load */
297 pa_idxset_remove_all(c->modules, (pa_free_cb_t) pa_module_free);
298
299 if (c->module_defer_unload_event) {
300 c->mainloop->defer_free(c->module_defer_unload_event);
301 c->module_defer_unload_event = NULL;
302 }
303 }
304
305 static void defer_cb(pa_mainloop_api*api, pa_defer_event *e, void *userdata) {
306 void *state = NULL;
307 pa_core *c = PA_CORE(userdata);
308 pa_module *m;
309
310 pa_core_assert_ref(c);
311 api->defer_enable(e, 0);
312
313 while ((m = pa_idxset_iterate(c->modules, &state, NULL)))
314 if (m->unload_requested)
315 pa_module_unload(c, m, true);
316 }
317
318 void pa_module_unload_request(pa_module *m, bool force) {
319 pa_assert(m);
320
321 if (m->core->disallow_module_loading && !force)
322 return;
323
324 m->unload_requested = true;
325
326 if (!m->core->module_defer_unload_event)
327 m->core->module_defer_unload_event = m->core->mainloop->defer_new(m->core->mainloop, defer_cb, m->core);
328
329 m->core->mainloop->defer_enable(m->core->module_defer_unload_event, 1);
330 }
331
332 void pa_module_unload_request_by_index(pa_core *c, uint32_t idx, bool force) {
333 pa_module *m;
334 pa_assert(c);
335
336 if (!(m = pa_idxset_get_by_index(c->modules, idx)))
337 return;
338
339 pa_module_unload_request(m, force);
340 }
341
342 int pa_module_get_n_used(pa_module*m) {
343 pa_assert(m);
344
345 if (!m->get_n_used)
346 return -1;
347
348 return m->get_n_used(m);
349 }
350
351 void pa_module_update_proplist(pa_module *m, pa_update_mode_t mode, pa_proplist *p) {
352 pa_assert(m);
353
354 if (p)
355 pa_proplist_update(m->proplist, mode, p);
356
357 pa_subscription_post(m->core, PA_SUBSCRIPTION_EVENT_MODULE|PA_SUBSCRIPTION_EVENT_CHANGE, m->index);
358 }