2 This file is part of PulseAudio.
4 Copyright 2004-2006 Lennart Poettering
5 Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
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.
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.
17 You should have received a copy of the GNU Lesser General Public License
18 along with PulseAudio; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
33 #include <pulse/xmalloc.h>
34 #include <pulse/proplist.h>
36 #include <pulsecore/core-subscribe.h>
37 #include <pulsecore/log.h>
38 #include <pulsecore/core-util.h>
39 #include <pulsecore/macro.h>
40 #include <pulsecore/ltdl-helper.h>
41 #include <pulsecore/modinfo.h>
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"
51 bool pa_module_exists(const char *name
) {
52 const char *paths
, *state
= NULL
;
53 char *n
, *p
, *pathname
;
58 if (name
[0] == PA_PATH_SEP_CHAR
) {
59 result
= access(name
, F_OK
) == 0 ? true : false;
60 pa_log_debug("Checking for existence of '%s': %s", name
, result
? "success" : "failure");
65 if (!(paths
= lt_dlgetsearchpath()))
68 /* strip .so from the end of name, if present */
71 if (p
&& pa_streq(p
, PA_SOEXT
))
74 while ((p
= pa_split(paths
, ":", &state
))) {
75 pathname
= pa_sprintf_malloc("%s" PA_PATH_SEP
"%s" PA_SOEXT
, p
, n
);
76 result
= access(pathname
, F_OK
) == 0 ? true : false;
77 pa_log_debug("Checking for existence of '%s': %s", pathname
, result
? "success" : "failure");
87 if (PA_UNLIKELY(pa_run_from_build_tree())) {
88 while ((p
= pa_split(paths
, ":", &state
))) {
89 pathname
= pa_sprintf_malloc("%s" PA_PATH_SEP
".libs" PA_PATH_SEP
"%s" PA_SOEXT
, p
, n
);
90 result
= access(pathname
, F_OK
) == 0 ? true : false;
91 pa_log_debug("Checking for existence of '%s': %s", pathname
, result
? "success" : "failure");
105 pa_module
* pa_module_load(pa_core
*c
, const char *name
, const char *argument
) {
107 bool (*load_once
)(void);
108 const char* (*get_deprecated
)(void);
114 if (c
->disallow_module_loading
)
117 m
= pa_xnew(pa_module
, 1);
118 m
->name
= pa_xstrdup(name
);
119 m
->argument
= pa_xstrdup(argument
);
120 m
->load_once
= false;
121 m
->proplist
= pa_proplist_new();
122 m
->index
= PA_IDXSET_INVALID
;
124 if (!(m
->dl
= lt_dlopenext(name
))) {
125 /* We used to print the error that is returned by lt_dlerror(), but
126 * lt_dlerror() is useless. It returns pretty much always "file not
127 * found". That's because if there are any problems with loading the
128 * module with normal loaders, libltdl falls back to the "preload"
129 * loader, which never finds anything, and therefore says "file not
131 pa_log("Failed to open module \"%s\".", name
);
135 if ((load_once
= (bool (*)(void)) pa_load_sym(m
->dl
, name
, PA_SYMBOL_LOAD_ONCE
))) {
137 m
->load_once
= load_once();
142 /* OK, the module only wants to be loaded once, let's make sure it is */
144 PA_IDXSET_FOREACH(i
, c
->modules
, idx
) {
145 if (pa_streq(name
, i
->name
)) {
146 pa_log("Module \"%s\" should be loaded once at most. Refusing to load.", name
);
153 if ((get_deprecated
= (const char* (*) (void)) pa_load_sym(m
->dl
, name
, PA_SYMBOL_GET_DEPRECATE
))) {
156 if ((t
= get_deprecated()))
157 pa_log_warn("%s is deprecated: %s", name
, t
);
160 if (!(m
->init
= (int (*)(pa_module
*_m
)) pa_load_sym(m
->dl
, name
, PA_SYMBOL_INIT
))) {
161 pa_log("Failed to load module \"%s\": symbol \""PA_SYMBOL_INIT
"\" not found.", name
);
165 m
->done
= (void (*)(pa_module
*_m
)) pa_load_sym(m
->dl
, name
, PA_SYMBOL_DONE
);
166 m
->get_n_used
= (int (*)(pa_module
*_m
)) pa_load_sym(m
->dl
, name
, PA_SYMBOL_GET_N_USED
);
169 m
->unload_requested
= false;
171 pa_assert_se(pa_idxset_put(c
->modules
, m
, &m
->index
) >= 0);
172 pa_assert(m
->index
!= PA_IDXSET_INVALID
);
174 if (m
->init(m
) < 0) {
175 pa_log_error("Failed to load module \"%s\" (argument: \"%s\"): initialization failed.", name
, argument
? argument
: "");
179 pa_log_info("Loaded \"%s\" (index: #%u; argument: \"%s\").", m
->name
, m
->index
, m
->argument
? m
->argument
: "");
181 pa_subscription_post(c
, PA_SUBSCRIPTION_EVENT_MODULE
|PA_SUBSCRIPTION_EVENT_NEW
, m
->index
);
183 if ((mi
= pa_modinfo_get_by_handle(m
->dl
, name
))) {
185 if (mi
->author
&& !pa_proplist_contains(m
->proplist
, PA_PROP_MODULE_AUTHOR
))
186 pa_proplist_sets(m
->proplist
, PA_PROP_MODULE_AUTHOR
, mi
->author
);
188 if (mi
->description
&& !pa_proplist_contains(m
->proplist
, PA_PROP_MODULE_DESCRIPTION
))
189 pa_proplist_sets(m
->proplist
, PA_PROP_MODULE_DESCRIPTION
, mi
->description
);
191 if (mi
->version
&& !pa_proplist_contains(m
->proplist
, PA_PROP_MODULE_VERSION
))
192 pa_proplist_sets(m
->proplist
, PA_PROP_MODULE_VERSION
, mi
->version
);
202 if (m
->index
!= PA_IDXSET_INVALID
)
203 pa_idxset_remove_by_index(c
->modules
, m
->index
);
206 pa_proplist_free(m
->proplist
);
208 pa_xfree(m
->argument
);
220 static void pa_module_free(pa_module
*m
) {
224 pa_log_info("Unloading \"%s\" (index: #%u).", m
->name
, m
->index
);
230 pa_proplist_free(m
->proplist
);
234 pa_log_info("Unloaded \"%s\" (index: #%u).", m
->name
, m
->index
);
236 pa_subscription_post(m
->core
, PA_SUBSCRIPTION_EVENT_MODULE
|PA_SUBSCRIPTION_EVENT_REMOVE
, m
->index
);
239 pa_xfree(m
->argument
);
243 void pa_module_unload(pa_core
*c
, pa_module
*m
, bool force
) {
247 if (m
->core
->disallow_module_loading
&& !force
)
250 if (!(m
= pa_idxset_remove_by_data(c
->modules
, m
, NULL
)))
256 void pa_module_unload_by_index(pa_core
*c
, uint32_t idx
, bool force
) {
259 pa_assert(idx
!= PA_IDXSET_INVALID
);
261 if (c
->disallow_module_loading
&& !force
)
264 if (!(m
= pa_idxset_remove_by_index(c
->modules
, idx
)))
270 void pa_module_unload_all(pa_core
*c
) {
277 pa_assert(c
->modules
);
279 if (pa_idxset_isempty(c
->modules
))
282 /* Unload modules in reverse order by default */
283 indices
= pa_xnew(uint32_t, pa_idxset_size(c
->modules
));
285 PA_IDXSET_FOREACH(m
, c
->modules
, state
)
286 indices
[i
++] = state
;
287 pa_assert(i
== (int) pa_idxset_size(c
->modules
));
289 for (; i
>= 0; i
--) {
290 m
= pa_idxset_remove_by_index(c
->modules
, indices
[i
]);
296 /* Just in case module unloading caused more modules to load */
297 pa_idxset_remove_all(c
->modules
, (pa_free_cb_t
) pa_module_free
);
299 if (c
->module_defer_unload_event
) {
300 c
->mainloop
->defer_free(c
->module_defer_unload_event
);
301 c
->module_defer_unload_event
= NULL
;
305 static void defer_cb(pa_mainloop_api
*api
, pa_defer_event
*e
, void *userdata
) {
307 pa_core
*c
= PA_CORE(userdata
);
310 pa_core_assert_ref(c
);
311 api
->defer_enable(e
, 0);
313 while ((m
= pa_idxset_iterate(c
->modules
, &state
, NULL
)))
314 if (m
->unload_requested
)
315 pa_module_unload(c
, m
, true);
318 void pa_module_unload_request(pa_module
*m
, bool force
) {
321 if (m
->core
->disallow_module_loading
&& !force
)
324 m
->unload_requested
= true;
326 if (!m
->core
->module_defer_unload_event
)
327 m
->core
->module_defer_unload_event
= m
->core
->mainloop
->defer_new(m
->core
->mainloop
, defer_cb
, m
->core
);
329 m
->core
->mainloop
->defer_enable(m
->core
->module_defer_unload_event
, 1);
332 void pa_module_unload_request_by_index(pa_core
*c
, uint32_t idx
, bool force
) {
336 if (!(m
= pa_idxset_get_by_index(c
->modules
, idx
)))
339 pa_module_unload_request(m
, force
);
342 int pa_module_get_n_used(pa_module
*m
) {
348 return m
->get_n_used(m
);
351 void pa_module_update_proplist(pa_module
*m
, pa_update_mode_t mode
, pa_proplist
*p
) {
355 pa_proplist_update(m
->proplist
, mode
, p
);
357 pa_subscription_post(m
->core
, PA_SUBSCRIPTION_EVENT_MODULE
|PA_SUBSCRIPTION_EVENT_CHANGE
, m
->index
);