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