• 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             goto fail;
215         }
216         AUDIO_ERR_LOG("Failed to load module \"%{public}s\" (argument: \"%{public}s\"): initialization failed.",
217             name, argument ? argument : "");
218         errcode = -PA_ERR_IO;
219         goto fail;
220     }
221 
222     pa_log_info("Loaded \"%s\" (index: #%u; argument: \"%s\").", m->name, m->index, m->argument ? m->argument : "");
223 
224     pa_subscription_post(c, PA_SUBSCRIPTION_EVENT_MODULE|PA_SUBSCRIPTION_EVENT_NEW, m->index);
225 
226     if ((mi = pa_modinfo_get_by_handle(m->dl, name))) {
227 
228         if (mi->author && !pa_proplist_contains(m->proplist, PA_PROP_MODULE_AUTHOR))
229             pa_proplist_sets(m->proplist, PA_PROP_MODULE_AUTHOR, mi->author);
230 
231         if (mi->description && !pa_proplist_contains(m->proplist, PA_PROP_MODULE_DESCRIPTION))
232             pa_proplist_sets(m->proplist, PA_PROP_MODULE_DESCRIPTION, mi->description);
233 
234         if (mi->version && !pa_proplist_contains(m->proplist, PA_PROP_MODULE_VERSION))
235             pa_proplist_sets(m->proplist, PA_PROP_MODULE_VERSION, mi->version);
236 
237         pa_modinfo_free(mi);
238     }
239 
240     pa_hook_fire(&m->core->hooks[PA_CORE_HOOK_MODULE_NEW], m);
241 
242     *module = m;
243 
244     return 0;
245 
246 fail:
247 
248     if (m) {
249         if (m->index != PA_IDXSET_INVALID)
250             pa_idxset_remove_by_index(c->modules, m->index);
251 
252         if (m->hooks)
253             pa_dynarray_free(m->hooks);
254 
255         if (m->proplist)
256             pa_proplist_free(m->proplist);
257 
258         pa_xfree(m->argument);
259         pa_xfree(m->name);
260 
261         if (m->dl)
262             lt_dlclose(m->dl);
263 
264         pa_xfree(m);
265     }
266 
267     *module = NULL;
268 
269     return errcode;
270 }
271 
postponed_dlclose(pa_mainloop_api * api,void * userdata)272 static void postponed_dlclose(pa_mainloop_api *api, void *userdata) {
273     lt_dlhandle dl = userdata;
274 
275     lt_dlclose(dl);
276 }
277 
pa_module_free(pa_module * m)278 static void pa_module_free(pa_module *m) {
279     pa_assert(m);
280     pa_assert(m->core);
281 
282     pa_log_info("Unloading \"%s\" (index: #%u).", m->name, m->index);
283     pa_hook_fire(&m->core->hooks[PA_CORE_HOOK_MODULE_UNLINK], m);
284 
285     if (m->hooks) {
286        pa_dynarray_free(m->hooks);
287        m->hooks = NULL;
288     }
289 
290     if (m->done)
291         m->done(m);
292 
293     if (m->proplist)
294         pa_proplist_free(m->proplist);
295 
296     /* If a module unloads itself with pa_module_unload(), we can't call
297      * lt_dlclose() here, because otherwise pa_module_unload() may return to a
298      * code location that has been removed from memory. Therefore, let's
299      * postpone the lt_dlclose() call a bit.
300      *
301      * Apparently lt_dlclose() doesn't always remove the module from memory,
302      * but it can happen, as can be seen here:
303      * https://bugs.freedesktop.org/show_bug.cgi?id=96831 */
304     pa_mainloop_api_once(m->core->mainloop, postponed_dlclose, m->dl);
305 
306     pa_hashmap_remove(m->core->modules_pending_unload, m);
307 
308     pa_log_info("Unloaded \"%s\" (index: #%u).", m->name, m->index);
309 
310     pa_subscription_post(m->core, PA_SUBSCRIPTION_EVENT_MODULE|PA_SUBSCRIPTION_EVENT_REMOVE, m->index);
311 
312     pa_xfree(m->name);
313     pa_xfree(m->argument);
314     pa_xfree(m);
315 }
316 
pa_module_unload(pa_module * m,bool force)317 void pa_module_unload(pa_module *m, bool force) {
318     pa_assert(m);
319 
320     if (m->core->disallow_module_loading && !force)
321         return;
322 
323     if (!(m = pa_idxset_remove_by_data(m->core->modules, m, NULL)))
324         return;
325 
326     pa_module_free(m);
327 }
328 
pa_module_unload_by_index(pa_core * c,uint32_t idx,bool force)329 void pa_module_unload_by_index(pa_core *c, uint32_t idx, bool force) {
330     pa_module *m;
331     pa_assert(c);
332     pa_assert(idx != PA_IDXSET_INVALID);
333 
334     if (c->disallow_module_loading && !force)
335         return;
336 
337     if (!(m = pa_idxset_remove_by_index(c->modules, idx)))
338         return;
339 
340     pa_module_free(m);
341 }
342 
pa_module_unload_all(pa_core * c)343 void pa_module_unload_all(pa_core *c) {
344     pa_module *m;
345     uint32_t *indices;
346     uint32_t state;
347     int i;
348 
349     pa_assert(c);
350     pa_assert(c->modules);
351 
352     if (pa_idxset_isempty(c->modules))
353         return;
354 
355     /* Unload modules in reverse order by default */
356     indices = pa_xnew(uint32_t, pa_idxset_size(c->modules));
357     i = 0;
358     PA_IDXSET_FOREACH(m, c->modules, state)
359         indices[i++] = state;
360     pa_assert(i == (int) pa_idxset_size(c->modules));
361     i--;
362     for (; i >= 0; i--) {
363         m = pa_idxset_remove_by_index(c->modules, indices[i]);
364         if (m)
365             pa_module_free(m);
366     }
367     pa_xfree(indices);
368 
369     /* Just in case module unloading caused more modules to load */
370     PA_IDXSET_FOREACH(m, c->modules, state)
371         pa_log_warn("After module unload, module '%s' was still loaded!", m->name);
372     c->disallow_module_loading = 1;
373     pa_idxset_remove_all(c->modules, (pa_free_cb_t) pa_module_free);
374     pa_assert(pa_idxset_isempty(c->modules));
375 
376     if (c->module_defer_unload_event) {
377         c->mainloop->defer_free(c->module_defer_unload_event);
378         c->module_defer_unload_event = NULL;
379     }
380     pa_assert(pa_hashmap_isempty(c->modules_pending_unload));
381 }
382 
defer_cb(pa_mainloop_api * api,pa_defer_event * e,void * userdata)383 static void defer_cb(pa_mainloop_api*api, pa_defer_event *e, void *userdata) {
384     pa_core *c = PA_CORE(userdata);
385     pa_module *m;
386 
387     pa_core_assert_ref(c);
388     api->defer_enable(e, 0);
389 
390     while ((m = pa_hashmap_first(c->modules_pending_unload)))
391         pa_module_unload(m, true);
392 }
393 
pa_module_unload_request(pa_module * m,bool force)394 void pa_module_unload_request(pa_module *m, bool force) {
395     pa_assert(m);
396 
397     if (m->core->disallow_module_loading && !force)
398         return;
399 
400     m->unload_requested = true;
401     pa_hashmap_put(m->core->modules_pending_unload, m, m);
402 
403     if (!m->core->module_defer_unload_event)
404         m->core->module_defer_unload_event = m->core->mainloop->defer_new(m->core->mainloop, defer_cb, m->core);
405 
406     m->core->mainloop->defer_enable(m->core->module_defer_unload_event, 1);
407 }
408 
pa_module_unload_request_by_index(pa_core * c,uint32_t idx,bool force)409 void pa_module_unload_request_by_index(pa_core *c, uint32_t idx, bool force) {
410     pa_module *m;
411     pa_assert(c);
412 
413     if (!(m = pa_idxset_get_by_index(c->modules, idx)))
414         return;
415 
416     pa_module_unload_request(m, force);
417 }
418 
pa_module_get_n_used(pa_module * m)419 int pa_module_get_n_used(pa_module*m) {
420     pa_assert(m);
421 
422     if (!m->get_n_used)
423         return -1;
424 
425     return m->get_n_used(m);
426 }
427 
pa_module_update_proplist(pa_module * m,pa_update_mode_t mode,pa_proplist * p)428 void pa_module_update_proplist(pa_module *m, pa_update_mode_t mode, pa_proplist *p) {
429     pa_assert(m);
430 
431     if (p)
432         pa_proplist_update(m->proplist, mode, p);
433 
434     pa_subscription_post(m->core, PA_SUBSCRIPTION_EVENT_MODULE|PA_SUBSCRIPTION_EVENT_CHANGE, m->index);
435     pa_hook_fire(&m->core->hooks[PA_CORE_HOOK_MODULE_PROPLIST_CHANGED], m);
436 }
437