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