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, see <http://www.gnu.org/licenses/>.
19 ***/
20
21 #ifdef HAVE_CONFIG_H
22 #include <config.h>
23 #endif
24
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <errno.h>
29 #include <ltdl.h>
30
31 #include <pulse/xmalloc.h>
32 #include <pulse/proplist.h>
33
34 #include <pulsecore/core-subscribe.h>
35 #include <pulsecore/log.h>
36 #include <pulsecore/core-util.h>
37 #include <pulsecore/macro.h>
38 #include <pulsecore/ltdl-helper.h>
39 #include <pulsecore/modinfo.h>
40
41 #include "module.h"
42
43 #define PA_SYMBOL_INIT "pa__init"
44 #define PA_SYMBOL_DONE "pa__done"
45 #define PA_SYMBOL_LOAD_ONCE "pa__load_once"
46 #define PA_SYMBOL_GET_N_USED "pa__get_n_used"
47 #define PA_SYMBOL_GET_DEPRECATE "pa__get_deprecated"
48
pa_module_exists(const char * name)49 bool pa_module_exists(const char *name) {
50 const char *paths, *state = NULL;
51 char *n, *p, *pathname;
52 bool result;
53
54 pa_assert(name);
55
56 if (name[0] == PA_PATH_SEP_CHAR) {
57 result = access(name, F_OK) == 0 ? true : false;
58 pa_log_debug("Checking for existence of '%s': %s", name, result ? "success" : "failure");
59 if (result)
60 return true;
61 }
62
63 if (!(paths = lt_dlgetsearchpath()))
64 return false;
65
66 /* strip .so from the end of name, if present */
67 n = pa_xstrdup(name);
68 p = strrchr(n, '.');
69 if (p && pa_streq(p, PA_SOEXT))
70 p[0] = 0;
71
72 while ((p = pa_split(paths, ":", &state))) {
73 pathname = pa_sprintf_malloc("%s" PA_PATH_SEP "%s" PA_SOEXT, p, n);
74 result = access(pathname, F_OK) == 0 ? true : false;
75 pa_log_debug("Checking for existence of '%s': %s", pathname, result ? "success" : "failure");
76 pa_xfree(pathname);
77 pa_xfree(p);
78 if (result) {
79 pa_xfree(n);
80 return true;
81 }
82 }
83
84 state = NULL;
85 if (PA_UNLIKELY(pa_run_from_build_tree())) {
86 while ((p = pa_split(paths, ":", &state))) {
87 #ifdef MESON_BUILD
88 pathname = pa_sprintf_malloc("%s" PA_PATH_SEP "src" PA_PATH_SEP "modules" PA_PATH_SEP "%s" PA_SOEXT, p, n);
89 #else
90 pathname = pa_sprintf_malloc("%s" PA_PATH_SEP ".libs" PA_PATH_SEP "%s" PA_SOEXT, p, n);
91 #endif
92 result = access(pathname, F_OK) == 0 ? true : false;
93 pa_log_debug("Checking for existence of '%s': %s", pathname, result ? "success" : "failure");
94 pa_xfree(pathname);
95 pa_xfree(p);
96 if (result) {
97 pa_xfree(n);
98 return true;
99 }
100 }
101 }
102
103 pa_xfree(n);
104 return false;
105 }
106
pa_module_hook_connect(pa_module * m,pa_hook * hook,pa_hook_priority_t prio,pa_hook_cb_t cb,void * data)107 void pa_module_hook_connect(pa_module *m, pa_hook *hook, pa_hook_priority_t prio, pa_hook_cb_t cb, void *data) {
108 pa_assert(m);
109 pa_assert(hook);
110 pa_assert(m->hooks);
111 pa_dynarray_append(m->hooks, pa_hook_connect(hook, prio, cb, data));
112 }
113
pa_module_load(pa_module ** module,pa_core * c,const char * name,const char * argument)114 int pa_module_load(pa_module** module, pa_core *c, const char *name, const char *argument) {
115 pa_module *m = NULL;
116 bool (*load_once)(void);
117 const char* (*get_deprecated)(void);
118 pa_modinfo *mi;
119 int errcode, rval;
120
121 pa_assert(module);
122 pa_assert(c);
123 pa_assert(name);
124
125 if (c->disallow_module_loading) {
126 errcode = -PA_ERR_ACCESS;
127 goto fail;
128 }
129
130 m = pa_xnew(pa_module, 1);
131 m->name = pa_xstrdup(name);
132 m->argument = pa_xstrdup(argument);
133 m->load_once = false;
134 m->proplist = pa_proplist_new();
135 m->hooks = pa_dynarray_new((pa_free_cb_t) pa_hook_slot_free);
136 m->index = PA_IDXSET_INVALID;
137
138 if (!(m->dl = lt_dlopenext(name))) {
139 /* We used to print the error that is returned by lt_dlerror(), but
140 * lt_dlerror() is useless. It returns pretty much always "file not
141 * found". That's because if there are any problems with loading the
142 * module with normal loaders, libltdl falls back to the "preload"
143 * loader, which never finds anything, and therefore says "file not
144 * found". */
145 pa_log("Failed to open module \"%s\".", name);
146 errcode = -PA_ERR_IO;
147 goto fail;
148 }
149
150 if ((load_once = (bool (*)(void)) pa_load_sym(m->dl, name, PA_SYMBOL_LOAD_ONCE))) {
151
152 m->load_once = load_once();
153
154 if (m->load_once) {
155 pa_module *i;
156 uint32_t idx;
157 /* OK, the module only wants to be loaded once, let's make sure it is */
158
159 PA_IDXSET_FOREACH(i, c->modules, idx) {
160 if (pa_streq(name, i->name)) {
161 pa_log("Module \"%s\" should be loaded once at most. Refusing to load.", name);
162 errcode = -PA_ERR_EXIST;
163 goto fail;
164 }
165 }
166 }
167 }
168
169 if ((get_deprecated = (const char* (*) (void)) pa_load_sym(m->dl, name, PA_SYMBOL_GET_DEPRECATE))) {
170 const char *t;
171
172 if ((t = get_deprecated()))
173 pa_log_warn("%s is deprecated: %s", name, t);
174 }
175
176 if (!(m->init = (int (*)(pa_module*_m)) pa_load_sym(m->dl, name, PA_SYMBOL_INIT))) {
177 pa_log("Failed to load module \"%s\": symbol \""PA_SYMBOL_INIT"\" not found.", name);
178 errcode = -PA_ERR_IO;
179 goto fail;
180 }
181
182 m->done = (void (*)(pa_module*_m)) pa_load_sym(m->dl, name, PA_SYMBOL_DONE);
183 m->get_n_used = (int (*)(pa_module*_m)) pa_load_sym(m->dl, name, PA_SYMBOL_GET_N_USED);
184 m->userdata = NULL;
185 m->core = c;
186 m->unload_requested = false;
187
188 pa_assert_se(pa_idxset_put(c->modules, m, &m->index) >= 0);
189 pa_assert(m->index != PA_IDXSET_INVALID);
190
191 if ((rval = m->init(m)) < 0) {
192 if (rval == -PA_MODULE_ERR_SKIP) {
193 errcode = -PA_ERR_NOENTITY;
194 goto fail;
195 }
196 pa_log_error("Failed to load module \"%s\" (argument: \"%s\"): initialization failed.", name, argument ? argument : "");
197 errcode = -PA_ERR_IO;
198 goto fail;
199 }
200
201 pa_log_info("Loaded \"%s\" (index: #%u; argument: \"%s\").", m->name, m->index, m->argument ? m->argument : "");
202
203 pa_subscription_post(c, PA_SUBSCRIPTION_EVENT_MODULE|PA_SUBSCRIPTION_EVENT_NEW, m->index);
204
205 if ((mi = pa_modinfo_get_by_handle(m->dl, name))) {
206
207 if (mi->author && !pa_proplist_contains(m->proplist, PA_PROP_MODULE_AUTHOR))
208 pa_proplist_sets(m->proplist, PA_PROP_MODULE_AUTHOR, mi->author);
209
210 if (mi->description && !pa_proplist_contains(m->proplist, PA_PROP_MODULE_DESCRIPTION))
211 pa_proplist_sets(m->proplist, PA_PROP_MODULE_DESCRIPTION, mi->description);
212
213 if (mi->version && !pa_proplist_contains(m->proplist, PA_PROP_MODULE_VERSION))
214 pa_proplist_sets(m->proplist, PA_PROP_MODULE_VERSION, mi->version);
215
216 pa_modinfo_free(mi);
217 }
218
219 pa_hook_fire(&m->core->hooks[PA_CORE_HOOK_MODULE_NEW], m);
220
221 *module = m;
222
223 return 0;
224
225 fail:
226
227 if (m) {
228 if (m->index != PA_IDXSET_INVALID)
229 pa_idxset_remove_by_index(c->modules, m->index);
230
231 if (m->hooks)
232 pa_dynarray_free(m->hooks);
233
234 if (m->proplist)
235 pa_proplist_free(m->proplist);
236
237 pa_xfree(m->argument);
238 pa_xfree(m->name);
239
240 if (m->dl)
241 lt_dlclose(m->dl);
242
243 pa_xfree(m);
244 }
245
246 *module = NULL;
247
248 return errcode;
249 }
250
postponed_dlclose(pa_mainloop_api * api,void * userdata)251 static void postponed_dlclose(pa_mainloop_api *api, void *userdata) {
252 lt_dlhandle dl = userdata;
253
254 lt_dlclose(dl);
255 }
256
pa_module_free(pa_module * m)257 static void pa_module_free(pa_module *m) {
258 pa_assert(m);
259 pa_assert(m->core);
260
261 pa_log_info("Unloading \"%s\" (index: #%u).", m->name, m->index);
262 pa_hook_fire(&m->core->hooks[PA_CORE_HOOK_MODULE_UNLINK], m);
263
264 if (m->hooks) {
265 pa_dynarray_free(m->hooks);
266 m->hooks = NULL;
267 }
268
269 if (m->done)
270 m->done(m);
271
272 if (m->proplist)
273 pa_proplist_free(m->proplist);
274
275 /* If a module unloads itself with pa_module_unload(), we can't call
276 * lt_dlclose() here, because otherwise pa_module_unload() may return to a
277 * code location that has been removed from memory. Therefore, let's
278 * postpone the lt_dlclose() call a bit.
279 *
280 * Apparently lt_dlclose() doesn't always remove the module from memory,
281 * but it can happen, as can be seen here:
282 * https://bugs.freedesktop.org/show_bug.cgi?id=96831 */
283 pa_mainloop_api_once(m->core->mainloop, postponed_dlclose, m->dl);
284
285 pa_hashmap_remove(m->core->modules_pending_unload, m);
286
287 pa_log_info("Unloaded \"%s\" (index: #%u).", m->name, m->index);
288
289 pa_subscription_post(m->core, PA_SUBSCRIPTION_EVENT_MODULE|PA_SUBSCRIPTION_EVENT_REMOVE, m->index);
290
291 pa_xfree(m->name);
292 pa_xfree(m->argument);
293 pa_xfree(m);
294 }
295
pa_module_unload(pa_module * m,bool force)296 void pa_module_unload(pa_module *m, bool force) {
297 pa_assert(m);
298
299 if (m->core->disallow_module_loading && !force)
300 return;
301
302 if (!(m = pa_idxset_remove_by_data(m->core->modules, m, NULL)))
303 return;
304
305 pa_module_free(m);
306 }
307
pa_module_unload_by_index(pa_core * c,uint32_t idx,bool force)308 void pa_module_unload_by_index(pa_core *c, uint32_t idx, bool force) {
309 pa_module *m;
310 pa_assert(c);
311 pa_assert(idx != PA_IDXSET_INVALID);
312
313 if (c->disallow_module_loading && !force)
314 return;
315
316 if (!(m = pa_idxset_remove_by_index(c->modules, idx)))
317 return;
318
319 pa_module_free(m);
320 }
321
pa_module_unload_all(pa_core * c)322 void pa_module_unload_all(pa_core *c) {
323 pa_module *m;
324 uint32_t *indices;
325 uint32_t state;
326 int i;
327
328 pa_assert(c);
329 pa_assert(c->modules);
330
331 if (pa_idxset_isempty(c->modules))
332 return;
333
334 /* Unload modules in reverse order by default */
335 indices = pa_xnew(uint32_t, pa_idxset_size(c->modules));
336 i = 0;
337 PA_IDXSET_FOREACH(m, c->modules, state)
338 indices[i++] = state;
339 pa_assert(i == (int) pa_idxset_size(c->modules));
340 i--;
341 for (; i >= 0; i--) {
342 m = pa_idxset_remove_by_index(c->modules, indices[i]);
343 if (m)
344 pa_module_free(m);
345 }
346 pa_xfree(indices);
347
348 /* Just in case module unloading caused more modules to load */
349 PA_IDXSET_FOREACH(m, c->modules, state)
350 pa_log_warn("After module unload, module '%s' was still loaded!", m->name);
351 c->disallow_module_loading = 1;
352 pa_idxset_remove_all(c->modules, (pa_free_cb_t) pa_module_free);
353 pa_assert(pa_idxset_isempty(c->modules));
354
355 if (c->module_defer_unload_event) {
356 c->mainloop->defer_free(c->module_defer_unload_event);
357 c->module_defer_unload_event = NULL;
358 }
359 pa_assert(pa_hashmap_isempty(c->modules_pending_unload));
360 }
361
defer_cb(pa_mainloop_api * api,pa_defer_event * e,void * userdata)362 static void defer_cb(pa_mainloop_api*api, pa_defer_event *e, void *userdata) {
363 pa_core *c = PA_CORE(userdata);
364 pa_module *m;
365
366 pa_core_assert_ref(c);
367 api->defer_enable(e, 0);
368
369 while ((m = pa_hashmap_first(c->modules_pending_unload)))
370 pa_module_unload(m, true);
371 }
372
pa_module_unload_request(pa_module * m,bool force)373 void pa_module_unload_request(pa_module *m, bool force) {
374 pa_assert(m);
375
376 if (m->core->disallow_module_loading && !force)
377 return;
378
379 m->unload_requested = true;
380 pa_hashmap_put(m->core->modules_pending_unload, m, m);
381
382 if (!m->core->module_defer_unload_event)
383 m->core->module_defer_unload_event = m->core->mainloop->defer_new(m->core->mainloop, defer_cb, m->core);
384
385 m->core->mainloop->defer_enable(m->core->module_defer_unload_event, 1);
386 }
387
pa_module_unload_request_by_index(pa_core * c,uint32_t idx,bool force)388 void pa_module_unload_request_by_index(pa_core *c, uint32_t idx, bool force) {
389 pa_module *m;
390 pa_assert(c);
391
392 if (!(m = pa_idxset_get_by_index(c->modules, idx)))
393 return;
394
395 pa_module_unload_request(m, force);
396 }
397
pa_module_get_n_used(pa_module * m)398 int pa_module_get_n_used(pa_module*m) {
399 pa_assert(m);
400
401 if (!m->get_n_used)
402 return -1;
403
404 return m->get_n_used(m);
405 }
406
pa_module_update_proplist(pa_module * m,pa_update_mode_t mode,pa_proplist * p)407 void pa_module_update_proplist(pa_module *m, pa_update_mode_t mode, pa_proplist *p) {
408 pa_assert(m);
409
410 if (p)
411 pa_proplist_update(m->proplist, mode, p);
412
413 pa_subscription_post(m->core, PA_SUBSCRIPTION_EVENT_MODULE|PA_SUBSCRIPTION_EVENT_CHANGE, m->index);
414 pa_hook_fire(&m->core->hooks[PA_CORE_HOOK_MODULE_PROPLIST_CHANGED], m);
415 }
416