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 50bool 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 108void 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 115int 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 242fail: 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 268static void postponed_dlclose(pa_mainloop_api *api, void *userdata) { 269 lt_dlhandle dl = userdata; 270 271 lt_dlclose(dl); 272} 273 274static 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 313void 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 325void 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 339void 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 379static 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 390void 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 405void 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 415int 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 424void 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