• 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 #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