mirror of
https://gitlab.freedesktop.org/pipewire/pipewire.git
synced 2025-11-03 09:01:54 -05:00
Make pw_get_client_name() return a static string. Unload the dbus interface. Rename the method to make it more obvious that the interface needs to be unloaded. Free module properties Free remote objects and types Free stream params and the array
365 lines
8.7 KiB
C
365 lines
8.7 KiB
C
/* PipeWire
|
|
* Copyright (C) 2016 Axis Communications <dev-gstreamer@axis.com>
|
|
* @author Linus Svensson <linus.svensson@axis.com>
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Library General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Library General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Library General Public
|
|
* License along with this library; if not, write to the
|
|
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
|
* Boston, MA 02110-1301, USA.
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#include <stdio.h>
|
|
#include <dlfcn.h>
|
|
#include <dirent.h>
|
|
#include <sys/stat.h>
|
|
#include <errno.h>
|
|
|
|
#include "pipewire/pipewire.h"
|
|
#include "pipewire/private.h"
|
|
#include "pipewire/interfaces.h"
|
|
#include "pipewire/utils.h"
|
|
#include "pipewire/module.h"
|
|
|
|
/** \cond */
|
|
struct impl {
|
|
struct pw_module this;
|
|
void *hnd;
|
|
};
|
|
|
|
struct resource_data {
|
|
struct spa_hook resource_listener;
|
|
};
|
|
|
|
|
|
/** \endcond */
|
|
|
|
static char *find_module(const char *path, const char *name)
|
|
{
|
|
char *filename;
|
|
struct dirent *entry;
|
|
struct stat s;
|
|
DIR *dir;
|
|
|
|
asprintf(&filename, "%s/%s.so", path, name);
|
|
|
|
if (stat(filename, &s) == 0 && S_ISREG(s.st_mode)) {
|
|
/* found a regular file with name */
|
|
return filename;
|
|
}
|
|
|
|
free(filename);
|
|
filename = NULL;
|
|
|
|
/* now recurse down in subdirectories and look for it there */
|
|
|
|
dir = opendir(path);
|
|
if (dir == NULL) {
|
|
pw_log_warn("could not open %s: %s", path, strerror(errno));
|
|
return NULL;
|
|
}
|
|
|
|
while ((entry = readdir(dir))) {
|
|
char *newpath;
|
|
|
|
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
|
|
continue;
|
|
|
|
asprintf(&newpath, "%s/%s", path, entry->d_name);
|
|
|
|
if (stat(newpath, &s) == 0 && S_ISDIR(s.st_mode)) {
|
|
filename = find_module(newpath, name);
|
|
}
|
|
free(newpath);
|
|
|
|
if (filename != NULL)
|
|
break;
|
|
}
|
|
|
|
closedir(dir);
|
|
|
|
return filename;
|
|
}
|
|
|
|
static void module_unbind_func(void *data)
|
|
{
|
|
struct pw_resource *resource = data;
|
|
spa_list_remove(&resource->link);
|
|
}
|
|
|
|
static const struct pw_resource_events resource_events = {
|
|
PW_VERSION_RESOURCE_EVENTS,
|
|
.destroy = module_unbind_func,
|
|
};
|
|
|
|
static void
|
|
global_bind(void *_data, struct pw_client *client, uint32_t permissions,
|
|
uint32_t version, uint32_t id)
|
|
{
|
|
struct pw_module *this = _data;
|
|
struct pw_global *global = this->global;
|
|
struct pw_resource *resource;
|
|
struct resource_data *data;
|
|
|
|
resource = pw_resource_new(client, id, permissions, global->type, version, sizeof(*data));
|
|
if (resource == NULL)
|
|
goto no_mem;
|
|
|
|
data = pw_resource_get_user_data(resource);
|
|
pw_resource_add_listener(resource, &data->resource_listener, &resource_events, resource);
|
|
|
|
pw_log_debug("module %p: bound to %d", this, resource->id);
|
|
|
|
spa_list_append(&this->resource_list, &resource->link);
|
|
|
|
this->info.change_mask = ~0;
|
|
pw_module_resource_info(resource, &this->info);
|
|
this->info.change_mask = 0;
|
|
|
|
return;
|
|
|
|
no_mem:
|
|
pw_log_error("can't create module resource");
|
|
pw_core_resource_error(client->core_resource,
|
|
client->core_resource->id, -ENOMEM, "no memory");
|
|
return;
|
|
}
|
|
|
|
static void global_destroy(void *object)
|
|
{
|
|
struct pw_module *module = object;
|
|
spa_hook_remove(&module->global_listener);
|
|
module->global = NULL;
|
|
pw_module_destroy(module);
|
|
}
|
|
|
|
static const struct pw_global_events global_events = {
|
|
PW_VERSION_GLOBAL_EVENTS,
|
|
.destroy = global_destroy,
|
|
.bind = global_bind,
|
|
};
|
|
|
|
/** Load a module
|
|
*
|
|
* \param core a \ref pw_core
|
|
* \param name name of the module to load
|
|
* \param args A string with arguments for the module
|
|
* \param[out] error Return location for an error string, or NULL
|
|
* \return A \ref pw_module if the module could be loaded, or NULL on failure.
|
|
*
|
|
* \memberof pw_module
|
|
*/
|
|
struct pw_module *
|
|
pw_module_load(struct pw_core *core,
|
|
const char *name, const char *args,
|
|
struct pw_client *owner,
|
|
struct pw_global *parent,
|
|
struct pw_properties *properties)
|
|
{
|
|
struct pw_module *this;
|
|
struct impl *impl;
|
|
void *hnd;
|
|
char *filename = NULL;
|
|
const char *module_dir;
|
|
int res;
|
|
pw_module_init_func_t init_func;
|
|
|
|
module_dir = getenv("PIPEWIRE_MODULE_DIR");
|
|
if (module_dir != NULL) {
|
|
char **l;
|
|
int i, n_paths;
|
|
|
|
pw_log_debug("PIPEWIRE_MODULE_DIR set to: %s", module_dir);
|
|
|
|
l = pw_split_strv(module_dir, "/", 0, &n_paths);
|
|
for (i = 0; l[i] != NULL; i++) {
|
|
filename = find_module(l[i], name);
|
|
if (filename != NULL)
|
|
break;
|
|
}
|
|
pw_free_strv(l);
|
|
} else {
|
|
pw_log_debug("moduledir set to: %s", MODULEDIR);
|
|
|
|
filename = find_module(MODULEDIR, name);
|
|
}
|
|
|
|
if (filename == NULL)
|
|
goto not_found;
|
|
|
|
pw_log_debug("trying to load module: %s (%s)", name, filename);
|
|
|
|
hnd = dlopen(filename, RTLD_NOW | RTLD_LOCAL);
|
|
|
|
if (hnd == NULL)
|
|
goto open_failed;
|
|
|
|
if ((init_func = dlsym(hnd, PIPEWIRE_SYMBOL_MODULE_INIT)) == NULL)
|
|
goto no_pw_module;
|
|
|
|
impl = calloc(1, sizeof(struct impl));
|
|
if (impl == NULL)
|
|
goto no_mem;
|
|
|
|
if (properties == NULL)
|
|
properties = pw_properties_new(NULL, NULL);
|
|
if (properties == NULL)
|
|
goto no_mem;
|
|
|
|
impl->hnd = hnd;
|
|
|
|
this = &impl->this;
|
|
this->core = core;
|
|
this->properties = properties;
|
|
|
|
spa_list_init(&this->resource_list);
|
|
spa_hook_list_init(&this->listener_list);
|
|
|
|
pw_properties_set(properties, PW_MODULE_PROP_NAME, name);
|
|
|
|
this->info.name = name ? strdup(name) : NULL;
|
|
this->info.filename = filename;
|
|
this->info.args = args ? strdup(args) : NULL;
|
|
this->info.props = &this->properties->dict;
|
|
|
|
spa_list_append(&core->module_list, &this->link);
|
|
|
|
this->global = pw_global_new(core,
|
|
PW_TYPE_INTERFACE_Module, PW_VERSION_MODULE,
|
|
pw_properties_new(
|
|
PW_MODULE_PROP_NAME, name,
|
|
NULL),
|
|
this);
|
|
|
|
if (this->global == NULL)
|
|
goto no_global;
|
|
|
|
pw_global_add_listener(this->global, &this->global_listener, &global_events, this);
|
|
this->info.id = this->global->id;
|
|
|
|
if ((res = init_func(this, args)) < 0)
|
|
goto init_failed;
|
|
|
|
pw_global_register(this->global, owner, parent);
|
|
|
|
pw_log_debug("loaded module: %s", this->info.name);
|
|
|
|
return this;
|
|
|
|
not_found:
|
|
pw_log_error("No module \"%s\" was found", name);
|
|
return NULL;
|
|
open_failed:
|
|
pw_log_error("Failed to open module: \"%s\" %s", filename, dlerror());
|
|
free(filename);
|
|
return NULL;
|
|
no_mem:
|
|
no_pw_module:
|
|
pw_log_error("\"%s\": is not a pipewire module", filename);
|
|
dlclose(hnd);
|
|
free(filename);
|
|
return NULL;
|
|
no_global:
|
|
pw_log_error("\"%s\": failed to create global", filename);
|
|
pw_module_destroy(this);
|
|
return NULL;
|
|
init_failed:
|
|
pw_log_error("\"%s\": failed to initialize: %s", filename, spa_strerror(res));
|
|
pw_module_destroy(this);
|
|
return NULL;
|
|
}
|
|
|
|
/** Destroy a module
|
|
* \param module the module to destroy
|
|
* \memberof pw_module
|
|
*/
|
|
void pw_module_destroy(struct pw_module *module)
|
|
{
|
|
struct impl *impl = SPA_CONTAINER_OF(module, struct impl, this);
|
|
struct pw_resource *resource, *tmp;
|
|
|
|
pw_log_debug("module %p: destroy", module);
|
|
pw_module_events_destroy(module);
|
|
|
|
spa_list_remove(&module->link);
|
|
|
|
if (module->global) {
|
|
spa_hook_remove(&module->global_listener);
|
|
pw_global_destroy(module->global);
|
|
}
|
|
spa_list_for_each_safe(resource, tmp, &module->resource_list, link)
|
|
pw_resource_destroy(resource);
|
|
|
|
if (module->info.name)
|
|
free((char *) module->info.name);
|
|
if (module->info.filename)
|
|
free((char *) module->info.filename);
|
|
if (module->info.args)
|
|
free((char *) module->info.args);
|
|
|
|
pw_properties_free(module->properties);
|
|
|
|
dlclose(impl->hnd);
|
|
free(impl);
|
|
}
|
|
|
|
struct pw_core *
|
|
pw_module_get_core(struct pw_module *module)
|
|
{
|
|
return module->core;
|
|
}
|
|
|
|
struct pw_global * pw_module_get_global(struct pw_module *module)
|
|
{
|
|
return module->global;
|
|
}
|
|
|
|
const struct pw_properties *pw_module_get_properties(struct pw_module *module)
|
|
{
|
|
return module->properties;
|
|
}
|
|
|
|
int pw_module_update_properties(struct pw_module *module, const struct spa_dict *dict)
|
|
{
|
|
struct pw_resource *resource;
|
|
uint32_t i;
|
|
|
|
for (i = 0; i < dict->n_items; i++)
|
|
pw_properties_set(module->properties, dict->items[i].key, dict->items[i].value);
|
|
|
|
module->info.props = &module->properties->dict;
|
|
|
|
module->info.change_mask |= PW_MODULE_CHANGE_MASK_PROPS;
|
|
spa_list_for_each(resource, &module->resource_list, link)
|
|
pw_module_resource_info(resource, &module->info);
|
|
module->info.change_mask = 0;
|
|
|
|
return 0;
|
|
}
|
|
|
|
const struct pw_module_info *
|
|
pw_module_get_info(struct pw_module *module)
|
|
{
|
|
return &module->info;
|
|
}
|
|
|
|
void pw_module_add_listener(struct pw_module *module,
|
|
struct spa_hook *listener,
|
|
const struct pw_module_events *events,
|
|
void *data)
|
|
{
|
|
spa_hook_list_append(&module->listener_list, listener, events, data);
|
|
}
|