mirror of
https://gitlab.freedesktop.org/pipewire/pipewire.git
synced 2025-11-01 22:58:50 -04:00
daemon: rework config parsing
Replace config parsing for something more flexible based on json.
This commit is contained in:
parent
1bd90dc666
commit
49d11acde0
7 changed files with 458 additions and 905 deletions
|
|
@ -1,445 +0,0 @@
|
|||
/* PipeWire
|
||||
* Copyright © 2016 Axis Communications <dev-gstreamer@axis.com>
|
||||
* @author Linus Svensson <linus.svensson@axis.com>
|
||||
* Copyright © 2018 Wim Taymans
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the next
|
||||
* paragraph) shall be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <limits.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#include <pipewire/impl.h>
|
||||
|
||||
#include "command.h"
|
||||
|
||||
/** \cond */
|
||||
|
||||
static struct pw_command *parse_command_help(struct pw_properties *properties, const char *line, char **err);
|
||||
static struct pw_command *parse_command_set_prop(struct pw_properties *properties, const char *line, char **err);
|
||||
static struct pw_command *parse_command_add_spa_lib(struct pw_properties *properties, const char *line, char **err);
|
||||
static struct pw_command *parse_command_module_load(struct pw_properties *properties, const char *line, char **err);
|
||||
static struct pw_command *parse_command_create_object(struct pw_properties *properties, const char *line, char **err);
|
||||
static struct pw_command *parse_command_exec(struct pw_properties *properties, const char *line, char **err);
|
||||
|
||||
struct impl {
|
||||
struct pw_command this;
|
||||
int first_arg;
|
||||
};
|
||||
|
||||
typedef struct pw_command *(*pw_command_parse_func_t) (struct pw_properties *properties, const char *line, char **err);
|
||||
|
||||
struct command_parse {
|
||||
const char *name;
|
||||
const char *description;
|
||||
pw_command_parse_func_t func;
|
||||
};
|
||||
|
||||
static const struct command_parse parsers[] = {
|
||||
{"help", "Show this help", parse_command_help},
|
||||
{"set-prop", "Set a property", parse_command_set_prop},
|
||||
{"add-spa-lib", "Add a library that provides a spa factory name regex", parse_command_add_spa_lib},
|
||||
{"load-module", "Load a module", parse_command_module_load},
|
||||
{"create-object", "Create an object from a factory", parse_command_create_object},
|
||||
{"exec", "Execute a program", parse_command_exec},
|
||||
{NULL, NULL, NULL }
|
||||
};
|
||||
|
||||
static const char whitespace[] = " \t";
|
||||
/** \endcond */
|
||||
|
||||
static int
|
||||
execute_command_help(struct pw_command *command, struct pw_context *context, char **err)
|
||||
{
|
||||
int i;
|
||||
|
||||
fputs("Available commands:\n", stdout);
|
||||
for (i = 0; parsers[i].name; i++)
|
||||
fprintf(stdout, " %20.20s\t%s\n", parsers[i].name, parsers[i].description);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct pw_command *parse_command_help(struct pw_properties *properties, const char *line, char **err)
|
||||
{
|
||||
struct impl *impl;
|
||||
struct pw_command *this;
|
||||
|
||||
impl = calloc(1, sizeof(struct impl));
|
||||
if (impl == NULL)
|
||||
goto no_mem;
|
||||
|
||||
this = &impl->this;
|
||||
this->func = execute_command_help;
|
||||
this->args = pw_split_strv(line, whitespace, 1, &this->n_args);
|
||||
|
||||
return this;
|
||||
|
||||
no_mem:
|
||||
*err = spa_aprintf("alloc failed: %m");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int
|
||||
execute_command_set_prop(struct pw_command *command, struct pw_context *context, char **err)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct pw_command *parse_command_set_prop(struct pw_properties *properties, const char *line, char **err)
|
||||
{
|
||||
struct impl *impl;
|
||||
struct pw_command *this;
|
||||
|
||||
impl = calloc(1, sizeof(struct impl));
|
||||
if (impl == NULL)
|
||||
goto error_alloc;
|
||||
|
||||
this = &impl->this;
|
||||
this->func = execute_command_set_prop;
|
||||
this->args = pw_split_strv(line, whitespace, 4, &this->n_args);
|
||||
|
||||
if (this->n_args < 3)
|
||||
goto error_arguments;
|
||||
|
||||
pw_log_debug("set property: '%s' = '%s'", this->args[1], this->args[2]);
|
||||
pw_properties_set(properties, this->args[1], this->args[2]);
|
||||
|
||||
if (strcmp(this->args[1], SPA_KEY_LOG_LEVEL) == 0) {
|
||||
pw_log_set_level(atoi(this->args[2]));
|
||||
setenv("PIPEWIRE_DEBUG", this->args[2], 1);
|
||||
}
|
||||
|
||||
return this;
|
||||
|
||||
error_arguments:
|
||||
*err = spa_aprintf("%s requires <property-name> <value>", this->args[0]);
|
||||
pw_free_strv(this->args);
|
||||
free(impl);
|
||||
return NULL;
|
||||
error_alloc:
|
||||
*err = spa_aprintf("alloc failed: %m");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int
|
||||
execute_command_add_spa_lib(struct pw_command *command, struct pw_context *context, char **err)
|
||||
{
|
||||
int res;
|
||||
|
||||
res = pw_context_add_spa_lib(context, command->args[1], command->args[2]);
|
||||
if (res < 0) {
|
||||
*err = spa_aprintf("could not add spa library \"%s\"", command->args[1]);
|
||||
return res;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct pw_command *parse_command_add_spa_lib(struct pw_properties *properties, const char *line, char **err)
|
||||
{
|
||||
struct impl *impl;
|
||||
struct pw_command *this;
|
||||
|
||||
impl = calloc(1, sizeof(struct impl));
|
||||
if (impl == NULL)
|
||||
goto no_mem;
|
||||
|
||||
this = &impl->this;
|
||||
this->func = execute_command_add_spa_lib;
|
||||
this->args = pw_split_strv(line, whitespace, 4, &this->n_args);
|
||||
|
||||
if (this->n_args < 3)
|
||||
goto no_library;
|
||||
|
||||
return this;
|
||||
|
||||
no_library:
|
||||
*err = spa_aprintf("%s requires <factory-regex> <library-name>", this->args[0]);
|
||||
pw_free_strv(this->args);
|
||||
free(impl);
|
||||
return NULL;
|
||||
no_mem:
|
||||
*err = spa_aprintf("alloc failed: %m");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static bool has_option(struct pw_command *this, int first_arg, const char *option)
|
||||
{
|
||||
int arg;
|
||||
for (arg = 1; arg < first_arg; arg++) {
|
||||
if (strstr(this->args[arg], "-") == this->args[arg]) {
|
||||
if (strcmp(this->args[arg], option) == 0)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static int
|
||||
execute_command_module_load(struct pw_command *command, struct pw_context *context, char **err)
|
||||
{
|
||||
struct pw_impl_module *module;
|
||||
struct impl *impl = SPA_CONTAINER_OF(command, struct impl, this);
|
||||
int arg = impl->first_arg;
|
||||
|
||||
module = pw_context_load_module(context, command->args[arg], command->args[arg+1], NULL);
|
||||
if (module == NULL) {
|
||||
if (errno == ENOENT && has_option(command, arg, "-ifexists")) {
|
||||
pw_log_debug("skipping unavailable module %s", command->args[arg]);
|
||||
return 0;
|
||||
}
|
||||
*err = spa_aprintf("could not load module \"%s\": %m", command->args[arg]);
|
||||
return -errno;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct pw_command *parse_command_module_load(struct pw_properties *properties, const char *line, char **err)
|
||||
{
|
||||
struct impl *impl;
|
||||
struct pw_command *this;
|
||||
int arg;
|
||||
|
||||
impl = calloc(1, sizeof(struct impl));
|
||||
if (impl == NULL)
|
||||
goto no_mem;
|
||||
|
||||
this = &impl->this;
|
||||
this->func = execute_command_module_load;
|
||||
|
||||
this->args = pw_split_strv(line, whitespace, INT_MAX, &this->n_args);
|
||||
|
||||
for (arg = 1; arg < this->n_args; arg++) {
|
||||
if (strstr(this->args[arg], "-") != this->args[arg])
|
||||
break;
|
||||
}
|
||||
if (arg + 1 > this->n_args)
|
||||
goto no_module;
|
||||
|
||||
pw_free_strv(this->args);
|
||||
this->args = pw_split_strv(line, whitespace, arg + 2, &this->n_args);
|
||||
|
||||
impl->first_arg = arg;
|
||||
|
||||
return this;
|
||||
|
||||
no_module:
|
||||
*err = spa_aprintf("%s requires a module name", this->args[0]);
|
||||
pw_free_strv(this->args);
|
||||
free(impl);
|
||||
return NULL;
|
||||
no_mem:
|
||||
*err = spa_aprintf("alloc failed: %m");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int
|
||||
execute_command_create_object(struct pw_command *command, struct pw_context *context, char **err)
|
||||
{
|
||||
struct pw_impl_factory *factory;
|
||||
struct impl *impl = SPA_CONTAINER_OF(command, struct impl, this);
|
||||
int arg = impl->first_arg;
|
||||
void *obj;
|
||||
|
||||
pw_log_debug("find factory %s", command->args[arg]);
|
||||
factory = pw_context_find_factory(context, command->args[arg]);
|
||||
if (factory == NULL) {
|
||||
if (has_option(command, arg, "-nofail"))
|
||||
return 0;
|
||||
pw_log_error("can't find factory %s", command->args[arg]);
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
pw_log_debug("create object with args %s", command->args[arg+1]);
|
||||
obj = pw_impl_factory_create_object(factory,
|
||||
NULL, NULL, 0,
|
||||
pw_properties_new_string(command->args[arg+1]),
|
||||
SPA_ID_INVALID);
|
||||
if (obj == NULL) {
|
||||
if (has_option(command, arg, "-nofail"))
|
||||
return 0;
|
||||
pw_log_error("can't create object from factory %s: %m", command->args[arg]);
|
||||
return -errno;
|
||||
}
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
static struct pw_command *parse_command_create_object(struct pw_properties *properties, const char *line, char **err)
|
||||
{
|
||||
struct impl *impl;
|
||||
struct pw_command *this;
|
||||
int arg;
|
||||
|
||||
impl = calloc(1, sizeof(struct impl));
|
||||
if (impl == NULL)
|
||||
goto no_mem;
|
||||
|
||||
this = &impl->this;
|
||||
this->func = execute_command_create_object;
|
||||
this->args = pw_split_strv(line, whitespace, INT_MAX, &this->n_args);
|
||||
|
||||
for (arg = 1; arg < this->n_args; arg++) {
|
||||
if (strstr(this->args[arg], "-") != this->args[arg])
|
||||
break;
|
||||
}
|
||||
if (arg > this->n_args)
|
||||
goto no_factory;
|
||||
|
||||
pw_free_strv(this->args);
|
||||
this->args = pw_split_strv(line, whitespace, arg + 2, &this->n_args);
|
||||
|
||||
impl->first_arg = arg;
|
||||
|
||||
return this;
|
||||
|
||||
no_factory:
|
||||
*err = spa_aprintf("%s requires <factory-name> [<key>=<value> ...]", this->args[0]);
|
||||
pw_free_strv(this->args);
|
||||
free(impl);
|
||||
return NULL;
|
||||
no_mem:
|
||||
*err = spa_aprintf("alloc failed: %m");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int
|
||||
execute_command_exec(struct pw_command *command, struct pw_context *context, char **err)
|
||||
{
|
||||
int pid, res;
|
||||
|
||||
pid = fork();
|
||||
|
||||
if (pid == 0) {
|
||||
pw_log_info("exec %s", command->args[1]);
|
||||
res = execvp(command->args[1], &command->args[1]);
|
||||
if (res == -1) {
|
||||
res = -errno;
|
||||
*err = spa_aprintf("'%s': %m", command->args[1]);
|
||||
return res;
|
||||
}
|
||||
}
|
||||
else {
|
||||
int status;
|
||||
res = waitpid(pid, &status, WNOHANG);
|
||||
pw_log_info("exec got pid %d res:%d status:%d", pid, res, status);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct pw_command *parse_command_exec(struct pw_properties *properties, const char *line, char **err)
|
||||
{
|
||||
struct impl *impl;
|
||||
struct pw_command *this;
|
||||
|
||||
impl = calloc(1, sizeof(struct impl));
|
||||
if (impl == NULL)
|
||||
goto no_mem;
|
||||
|
||||
this = &impl->this;
|
||||
this->func = execute_command_exec;
|
||||
this->args = pw_split_strv(line, whitespace, INT_MAX, &this->n_args);
|
||||
|
||||
if (this->n_args < 1)
|
||||
goto no_executable;
|
||||
|
||||
return this;
|
||||
|
||||
no_executable:
|
||||
*err = spa_aprintf("requires an executable name");
|
||||
pw_free_strv(this->args);
|
||||
free(impl);
|
||||
return NULL;
|
||||
no_mem:
|
||||
*err = spa_aprintf("alloc failed: %m");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/** Free command
|
||||
*
|
||||
* \param command a command to free
|
||||
*
|
||||
* Free all resources assicated with \a command.
|
||||
*
|
||||
* \memberof pw_command
|
||||
*/
|
||||
SPA_EXPORT
|
||||
void pw_command_free(struct pw_command *command)
|
||||
{
|
||||
struct impl *impl = SPA_CONTAINER_OF(command, struct impl, this);
|
||||
|
||||
spa_list_remove(&command->link);
|
||||
pw_free_strv(command->args);
|
||||
free(impl);
|
||||
}
|
||||
|
||||
/** Parses a command line
|
||||
* \param line command line to parse
|
||||
* \param[out] err Return location for an error
|
||||
* \return The command or NULL when \a err is set.
|
||||
*
|
||||
* Parses a command line, \a line, and return the parsed command.
|
||||
* A command can later be executed with \ref pw_command_run()
|
||||
*
|
||||
* \memberof pw_command
|
||||
*/
|
||||
SPA_EXPORT
|
||||
struct pw_command *pw_command_parse(struct pw_properties *properties, const char *line, char **err)
|
||||
{
|
||||
struct pw_command *command = NULL;
|
||||
const struct command_parse *parse;
|
||||
char *name;
|
||||
size_t len;
|
||||
|
||||
len = strcspn(line, whitespace);
|
||||
|
||||
name = strndup(line, len);
|
||||
|
||||
for (parse = parsers; parse->name != NULL; parse++) {
|
||||
if (strcmp(name, parse->name) == 0) {
|
||||
command = parse->func(properties, line, err);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
*err = spa_aprintf("Command \"%s\" does not exist", name);
|
||||
out:
|
||||
free(name);
|
||||
return command;
|
||||
}
|
||||
|
||||
/** Run a command
|
||||
*
|
||||
* \param command A \ref pw_command
|
||||
* \param context A \ref pw_context
|
||||
* \param err Return location for an error string, or NULL
|
||||
* \return 0 on success, < 0 on error
|
||||
*
|
||||
* \memberof pw_command
|
||||
*/
|
||||
SPA_EXPORT
|
||||
int pw_command_run(struct pw_command *command, struct pw_context *context, char **err)
|
||||
{
|
||||
return command->func(command, context, err);
|
||||
}
|
||||
|
|
@ -1,63 +0,0 @@
|
|||
/* PipeWire
|
||||
* Copyright © 2016 Axis Communications <dev-gstreamer@axis.com>
|
||||
* @author Linus Svensson <linus.svensson@axis.com>
|
||||
* Copyright © 2018 Wim Taymans
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the next
|
||||
* paragraph) shall be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef PIPEWIRE_COMMAND_H
|
||||
#define PIPEWIRE_COMMAND_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <pipewire/context.h>
|
||||
|
||||
struct pw_command;
|
||||
|
||||
typedef int (*pw_command_func_t) (struct pw_command *command, struct pw_context *context, char **err);
|
||||
|
||||
/** \class pw_command
|
||||
*
|
||||
* A configuration command
|
||||
*/
|
||||
struct pw_command {
|
||||
struct spa_list link; /**< link in list of commands */
|
||||
pw_command_func_t func;
|
||||
char **args;
|
||||
uint32_t id; /**< id of command */
|
||||
int n_args;
|
||||
};
|
||||
|
||||
struct pw_command *
|
||||
pw_command_parse(struct pw_properties *properties, const char *line, char **err);
|
||||
|
||||
void
|
||||
pw_command_free(struct pw_command *command);
|
||||
|
||||
int pw_command_run(struct pw_command *command, struct pw_context *context, char **err);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* PIPEWIRE_COMMAND_H */
|
||||
|
|
@ -1,212 +0,0 @@
|
|||
/* PipeWire
|
||||
* Copyright © 2016 Axis Communications <dev-gstreamer@axis.com>
|
||||
* @author Linus Svensson <linus.svensson@axis.com>
|
||||
* Copyright © 2018 Wim Taymans
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the next
|
||||
* paragraph) shall be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <pipewire/pipewire.h>
|
||||
|
||||
#include "daemon/command.h"
|
||||
#include "daemon/daemon-config.h"
|
||||
|
||||
#define DEFAULT_CONFIG_FILE PIPEWIRE_CONFIG_DIR "/pipewire.conf"
|
||||
|
||||
static int
|
||||
parse_line(struct pw_daemon_config *config,
|
||||
const char *filename, char *line, unsigned int lineno, char **err)
|
||||
{
|
||||
struct pw_command *command = NULL;
|
||||
char *p;
|
||||
char *local_err = NULL;
|
||||
|
||||
/* search for comments */
|
||||
if ((p = strchr(line, '#')))
|
||||
*p = '\0';
|
||||
|
||||
/* remove whitespaces */
|
||||
line = pw_strip(line, "\n\r \t");
|
||||
if (*line == '\0') /* empty line */
|
||||
goto out;
|
||||
|
||||
if ((command = pw_command_parse(config->properties, line, &local_err)) == NULL)
|
||||
goto error_parse;
|
||||
|
||||
spa_list_append(&config->commands, &command->link);
|
||||
|
||||
out:
|
||||
return 0;
|
||||
|
||||
error_parse:
|
||||
*err = spa_aprintf("%s:%u: %s", filename, lineno, local_err);
|
||||
free(local_err);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/**
|
||||
* pw_daemon_config_new:
|
||||
*
|
||||
* Returns a new empty #struct pw_daemon_config.
|
||||
*/
|
||||
struct pw_daemon_config *pw_daemon_config_new(struct pw_properties *properties)
|
||||
{
|
||||
struct pw_daemon_config *config;
|
||||
|
||||
config = calloc(1, sizeof(struct pw_daemon_config));
|
||||
if (config == NULL)
|
||||
goto error_exit;
|
||||
|
||||
config->properties = properties;
|
||||
spa_list_init(&config->commands);
|
||||
|
||||
return config;
|
||||
|
||||
error_exit:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* pw_daemon_config_free:
|
||||
* @config: A #struct pw_daemon_config
|
||||
*
|
||||
* Free all resources associated to @config.
|
||||
*/
|
||||
void pw_daemon_config_free(struct pw_daemon_config *config)
|
||||
{
|
||||
struct pw_command *cmd;
|
||||
|
||||
spa_list_consume(cmd, &config->commands, link)
|
||||
pw_command_free(cmd);
|
||||
|
||||
free(config);
|
||||
}
|
||||
|
||||
/**
|
||||
* pw_daemon_config_load_file:
|
||||
* @config: A #struct pw_daemon_config
|
||||
* @filename: A filename
|
||||
* @err: Return location for an error string
|
||||
*
|
||||
* Loads PipeWire config from @filename.
|
||||
*
|
||||
* Returns: 0 on success, otherwise < 0 and @err is set.
|
||||
*/
|
||||
int pw_daemon_config_load_file(struct pw_daemon_config *config, const char *filename, char **err)
|
||||
{
|
||||
unsigned int line;
|
||||
FILE *f;
|
||||
char buf[4096];
|
||||
|
||||
pw_log_debug("deamon-config %p: loading configuration file '%s'", config, filename);
|
||||
|
||||
if ((f = fopen(filename, "r")) == NULL) {
|
||||
*err = spa_aprintf("failed to open configuration file '%s': %s", filename,
|
||||
strerror(errno));
|
||||
goto open_error;
|
||||
}
|
||||
|
||||
line = 0;
|
||||
|
||||
while (!feof(f)) {
|
||||
if (!fgets(buf, sizeof(buf), f)) {
|
||||
if (feof(f))
|
||||
break;
|
||||
*err = spa_aprintf("failed to read configuration file '%s': %s",
|
||||
filename, strerror(errno));
|
||||
goto read_error;
|
||||
}
|
||||
|
||||
line++;
|
||||
|
||||
if (parse_line(config, filename, buf, line, err) != 0)
|
||||
goto parse_failed;
|
||||
}
|
||||
fclose(f);
|
||||
|
||||
return 0;
|
||||
|
||||
parse_failed:
|
||||
read_error:
|
||||
fclose(f);
|
||||
open_error:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/**
|
||||
* pw_daemon_config_load:
|
||||
* @config: A #struct pw_daemon_config
|
||||
* @err: Return location for a #GError, or %NULL
|
||||
*
|
||||
* Loads the default config file for PipeWire. The filename can be overridden with
|
||||
* an environment variable PIPEWIRE_CONFIG_FILE.
|
||||
*
|
||||
* Return: 0 on success, otherwise < 0 and @err is set.
|
||||
*/
|
||||
int pw_daemon_config_load(struct pw_daemon_config *config, char **err)
|
||||
{
|
||||
const char *filename;
|
||||
|
||||
filename = getenv("PIPEWIRE_CONFIG_FILE");
|
||||
if (filename != NULL && *filename != '\0') {
|
||||
pw_log_debug("PIPEWIRE_CONFIG_FILE set to: %s", filename);
|
||||
} else {
|
||||
filename = DEFAULT_CONFIG_FILE;
|
||||
}
|
||||
return pw_daemon_config_load_file(config, filename, err);
|
||||
}
|
||||
|
||||
/**
|
||||
* pw_daemon_config_run_commands:
|
||||
* @config: A #struct pw_daemon_config
|
||||
* @context: A #struct pw_context
|
||||
*
|
||||
* Run all commands that have been parsed. The list of commands will be cleared
|
||||
* when this function has been called.
|
||||
*
|
||||
* Returns: 0 if all commands where executed with success, otherwise < 0.
|
||||
*/
|
||||
int pw_daemon_config_run_commands(struct pw_daemon_config *config, struct pw_context *context)
|
||||
{
|
||||
char *err = NULL;
|
||||
int ret = 0;
|
||||
struct pw_command *command;
|
||||
|
||||
spa_list_for_each(command, &config->commands, link) {
|
||||
if ((ret = pw_command_run(command, context, &err)) < 0) {
|
||||
pw_log_error("could not run command %s: %s", command->args[0], err);
|
||||
free(err);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
spa_list_consume(command, &config->commands, link)
|
||||
pw_command_free(command);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -1,54 +0,0 @@
|
|||
/* PipeWire
|
||||
* Copyright © 2016 Axis Communications <dev-gstreamer@axis.com>
|
||||
* @author Linus Svensson <linus.svensson@axis.com>
|
||||
* Copyright © 2018 Wim Taymans
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the next
|
||||
* paragraph) shall be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef PIPEWIRE_DAEMON_CONFIG_H
|
||||
#define PIPEWIRE_DAEMON_CONFIG_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <pipewire/context.h>
|
||||
|
||||
struct pw_daemon_config {
|
||||
struct spa_list commands;
|
||||
struct pw_properties *properties;
|
||||
};
|
||||
|
||||
struct pw_daemon_config * pw_daemon_config_new(struct pw_properties *properties);
|
||||
|
||||
void pw_daemon_config_free(struct pw_daemon_config *config);
|
||||
|
||||
int pw_daemon_config_load_file(struct pw_daemon_config *config, const char *filename, char **err);
|
||||
|
||||
int pw_daemon_config_load(struct pw_daemon_config *config, char **err);
|
||||
|
||||
int pw_daemon_config_run_commands(struct pw_daemon_config *config, struct pw_context *context);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* PIPEWIRE_DAEMON_CONFIG_H */
|
||||
|
|
@ -24,39 +24,341 @@
|
|||
|
||||
#include <signal.h>
|
||||
#include <getopt.h>
|
||||
#include <limits.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/types.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#include <spa/utils/result.h>
|
||||
#include <spa/utils/json.h>
|
||||
|
||||
#include <pipewire/impl.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "daemon-config.h"
|
||||
|
||||
static const char *daemon_name;
|
||||
#define NAME "daemon"
|
||||
|
||||
#define DEFAULT_CONFIG_FILE "pipewire.conf"
|
||||
|
||||
struct data {
|
||||
struct pw_context *context;
|
||||
struct pw_main_loop *loop;
|
||||
|
||||
const char *daemon_name;
|
||||
struct pw_properties *conf;
|
||||
};
|
||||
|
||||
static int load_config(struct data *d)
|
||||
{
|
||||
const char *path;
|
||||
char filename[PATH_MAX], *data;
|
||||
struct stat sbuf;
|
||||
int fd;
|
||||
|
||||
path = getenv("PIPEWIRE_CONFIG_FILE");
|
||||
if (path == NULL) {
|
||||
const char *dir;
|
||||
dir = getenv("PIPEWIRE_CONFIG_DIR");
|
||||
if (dir == NULL)
|
||||
dir = PIPEWIRE_CONFIG_DIR;
|
||||
if (dir == NULL)
|
||||
return -ENOENT;
|
||||
|
||||
snprintf(filename, sizeof(filename), "%s/%s",
|
||||
dir, DEFAULT_CONFIG_FILE);
|
||||
path = filename;
|
||||
}
|
||||
|
||||
if ((fd = open(path, O_CLOEXEC | O_RDONLY)) < 0) {
|
||||
pw_log_warn(NAME" %p: error loading config '%s': %m", d, path);
|
||||
return -errno;
|
||||
}
|
||||
|
||||
pw_log_info(NAME" %p: loading config '%s'", d, path);
|
||||
if (fstat(fd, &sbuf) < 0)
|
||||
goto error_close;
|
||||
if ((data = mmap(NULL, sbuf.st_size, PROT_READ, MAP_PRIVATE, fd, 0)) == MAP_FAILED)
|
||||
goto error_close;
|
||||
close(fd);
|
||||
|
||||
pw_properties_update_string(d->conf, data, sbuf.st_size);
|
||||
munmap(data, sbuf.st_size);
|
||||
|
||||
return 0;
|
||||
|
||||
error_close:
|
||||
close(fd);
|
||||
return -errno;
|
||||
}
|
||||
|
||||
static int parse_spa_libs(struct data *d, const char *str)
|
||||
{
|
||||
struct spa_json it[2];
|
||||
char key[512], value[512];
|
||||
|
||||
spa_json_init(&it[0], str, strlen(str));
|
||||
if (spa_json_enter_object(&it[0], &it[1]) < 0)
|
||||
return -EINVAL;
|
||||
|
||||
while (spa_json_get_string(&it[1], key, sizeof(key)-1) > 0) {
|
||||
const char *val;
|
||||
if (key[0] == '#') {
|
||||
if (spa_json_next(&it[1], &val) <= 0)
|
||||
break;
|
||||
}
|
||||
else if (spa_json_get_string(&it[1], value, sizeof(value)-1) > 0) {
|
||||
pw_context_add_spa_lib(d->context, key, value);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int load_module(struct data *d, const char *key, const char *args, const char *flags)
|
||||
{
|
||||
if (pw_context_load_module(d->context, key, args, NULL) == NULL) {
|
||||
if (errno == ENOENT && flags && strstr(flags, "ifexists") != NULL) {
|
||||
pw_log_debug(NAME" %p: skipping unavailable module %s",
|
||||
d, key);
|
||||
} else {
|
||||
pw_log_error(NAME" %p: could not load module \"%s\": %m",
|
||||
d, key);
|
||||
return -errno;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int parse_modules(struct data *d, const char *str)
|
||||
{
|
||||
struct spa_json it[3];
|
||||
char key[512];
|
||||
int res = 0;
|
||||
|
||||
spa_json_init(&it[0], str, strlen(str));
|
||||
if (spa_json_enter_object(&it[0], &it[1]) < 0)
|
||||
return -EINVAL;
|
||||
|
||||
while (spa_json_get_string(&it[1], key, sizeof(key)-1) > 0) {
|
||||
const char *val;
|
||||
char *args = NULL, *flags = NULL;
|
||||
int len;
|
||||
|
||||
if ((len = spa_json_next(&it[1], &val)) <= 0)
|
||||
break;
|
||||
|
||||
if (key[0] == '#')
|
||||
continue;
|
||||
|
||||
if (spa_json_is_object(val, len)) {
|
||||
char arg[512], aval[1024];
|
||||
|
||||
spa_json_enter(&it[1], &it[2]);
|
||||
|
||||
while (spa_json_get_string(&it[2], arg, sizeof(arg)-1) > 0) {
|
||||
if (spa_json_get_string(&it[2], aval, sizeof(aval)-1) <= 0)
|
||||
break;
|
||||
|
||||
if (strcmp(arg, "args") == 0) {
|
||||
args = strdup(aval);
|
||||
} else if (strcmp(arg, "flags") == 0) {
|
||||
flags = strdup(aval);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (!spa_json_is_null(val, len))
|
||||
break;
|
||||
|
||||
res = load_module(d, key, args, flags);
|
||||
|
||||
free(args);
|
||||
free(flags);
|
||||
|
||||
if (res < 0)
|
||||
break;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static int create_object(struct data *d, const char *key, const char *args, const char *flags)
|
||||
{
|
||||
struct pw_impl_factory *factory;
|
||||
void *obj;
|
||||
|
||||
pw_log_debug("find factory %s", key);
|
||||
factory = pw_context_find_factory(d->context, key);
|
||||
if (factory == NULL) {
|
||||
if (flags && strstr(flags, "nofail") != NULL)
|
||||
return 0;
|
||||
pw_log_error("can't find factory %s", key);
|
||||
return -ENOENT;
|
||||
}
|
||||
pw_log_debug("create object with args %s", args);
|
||||
obj = pw_impl_factory_create_object(factory,
|
||||
NULL, NULL, 0,
|
||||
args ? pw_properties_new_string(args) : NULL,
|
||||
SPA_ID_INVALID);
|
||||
if (obj == NULL) {
|
||||
if (flags && strstr(flags, "nofail") != NULL)
|
||||
return 0;
|
||||
pw_log_error("can't create object from factory %s: %m", key);
|
||||
return -errno;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int parse_objects(struct data *d, const char *str)
|
||||
{
|
||||
struct spa_json it[3];
|
||||
char key[512];
|
||||
int res = 0;
|
||||
|
||||
spa_json_init(&it[0], str, strlen(str));
|
||||
if (spa_json_enter_object(&it[0], &it[1]) < 0)
|
||||
return -EINVAL;
|
||||
|
||||
while (spa_json_get_string(&it[1], key, sizeof(key)-1) > 0) {
|
||||
const char *val;
|
||||
char *args = NULL, *flags = NULL;
|
||||
int len;
|
||||
|
||||
if ((len = spa_json_next(&it[1], &val)) <= 0)
|
||||
break;
|
||||
|
||||
if (key[0] == '#')
|
||||
continue;
|
||||
|
||||
if (spa_json_is_object(val, len)) {
|
||||
char arg[512], aval[1024];
|
||||
|
||||
spa_json_enter(&it[1], &it[2]);
|
||||
|
||||
while (spa_json_get_string(&it[2], arg, sizeof(arg)-1) > 0) {
|
||||
if (spa_json_get_string(&it[2], aval, sizeof(aval)-1) <= 0)
|
||||
break;
|
||||
|
||||
if (strcmp(arg, "args") == 0) {
|
||||
args = strdup(aval);
|
||||
} else if (strcmp(arg, "flags") == 0) {
|
||||
flags = strdup(aval);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (!spa_json_is_null(val, len))
|
||||
break;
|
||||
|
||||
res = create_object(d, key, args, flags);
|
||||
|
||||
free(args);
|
||||
free(flags);
|
||||
|
||||
if (res < 0)
|
||||
break;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static int do_exec(struct data *d, const char *key, const char *args)
|
||||
{
|
||||
int pid, res, n_args;
|
||||
|
||||
pid = fork();
|
||||
|
||||
if (pid == 0) {
|
||||
char *cmd, **argv;
|
||||
|
||||
cmd = spa_aprintf("%s %s", key, args);
|
||||
argv = pw_split_strv(cmd, " \t", INT_MAX, &n_args);
|
||||
free(cmd);
|
||||
|
||||
pw_log_info("exec %s '%s'", key, args);
|
||||
res = execvp(key, argv);
|
||||
pw_free_strv(argv);
|
||||
|
||||
if (res == -1) {
|
||||
res = -errno;
|
||||
pw_log_error("execvp error '%s': %m", key);
|
||||
return res;
|
||||
}
|
||||
}
|
||||
else {
|
||||
int status;
|
||||
res = waitpid(pid, &status, WNOHANG);
|
||||
pw_log_info("exec got pid %d res:%d status:%d", pid, res, status);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int parse_exec(struct data *d, const char *str)
|
||||
{
|
||||
struct spa_json it[3];
|
||||
char key[512];
|
||||
int res = 0;
|
||||
|
||||
spa_json_init(&it[0], str, strlen(str));
|
||||
if (spa_json_enter_object(&it[0], &it[1]) < 0)
|
||||
return -EINVAL;
|
||||
|
||||
while (spa_json_get_string(&it[1], key, sizeof(key)-1) > 0) {
|
||||
const char *val;
|
||||
char *args = NULL;
|
||||
int len;
|
||||
|
||||
if ((len = spa_json_next(&it[1], &val)) <= 0)
|
||||
break;
|
||||
|
||||
if (key[0] == '#')
|
||||
continue;
|
||||
|
||||
if (spa_json_is_object(val, len)) {
|
||||
char arg[512], aval[1024];
|
||||
|
||||
spa_json_enter(&it[1], &it[2]);
|
||||
|
||||
while (spa_json_get_string(&it[2], arg, sizeof(arg)-1) > 0) {
|
||||
if (spa_json_get_string(&it[2], aval, sizeof(aval)-1) <= 0)
|
||||
break;
|
||||
|
||||
if (strcmp(arg, "args") == 0)
|
||||
args = strdup(aval);
|
||||
}
|
||||
}
|
||||
else if (!spa_json_is_null(val, len))
|
||||
break;
|
||||
|
||||
res = do_exec(d, key, args);
|
||||
|
||||
free(args);
|
||||
|
||||
if (res < 0)
|
||||
break;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static void do_quit(void *data, int signal_number)
|
||||
{
|
||||
struct pw_main_loop *loop = data;
|
||||
pw_main_loop_quit(loop);
|
||||
struct data *d = data;
|
||||
pw_main_loop_quit(d->loop);
|
||||
}
|
||||
|
||||
static void show_help(const char *name)
|
||||
static void show_help(struct data *d, const char *name)
|
||||
{
|
||||
fprintf(stdout, "%s [options]\n"
|
||||
" -h, --help Show this help\n"
|
||||
" --version Show version\n"
|
||||
" -n, --name Daemon name (Default %s)\n",
|
||||
name,
|
||||
daemon_name);
|
||||
d->daemon_name);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
struct pw_context *context;
|
||||
struct pw_main_loop *loop;
|
||||
struct pw_daemon_config *config;
|
||||
struct data d;
|
||||
struct pw_properties *properties;
|
||||
char *err = NULL;
|
||||
const char *str;
|
||||
static const struct option long_options[] = {
|
||||
{ "help", no_argument, NULL, 'h' },
|
||||
{ "version", no_argument, NULL, 'V' },
|
||||
|
|
@ -66,19 +368,30 @@ int main(int argc, char *argv[])
|
|||
};
|
||||
int c, res;
|
||||
|
||||
if (setenv("PIPEWIRE_INTERNAL", "1", 1) < 0)
|
||||
fprintf(stderr, "can't set PIPEWIRE_INTERNAL env: %m");
|
||||
|
||||
spa_zero(d);
|
||||
pw_init(&argc, &argv);
|
||||
|
||||
if (setenv("PIPEWIRE_INTERNAL", "1", 1) < 0)
|
||||
fprintf(stderr, "can't PIPEWIRE_INTERNAL env: %m");
|
||||
if ((d.conf = pw_properties_new(NULL, NULL)) == NULL) {
|
||||
pw_log_error("failed to create config: %m");
|
||||
return -1;
|
||||
}
|
||||
|
||||
daemon_name = getenv("PIPEWIRE_CORE");
|
||||
if (daemon_name == NULL)
|
||||
daemon_name = PW_DEFAULT_REMOTE;
|
||||
if ((res = load_config(&d)) < 0) {
|
||||
pw_log_error("failed to load config: %s", spa_strerror(res));
|
||||
return -1;
|
||||
}
|
||||
|
||||
d.daemon_name = getenv("PIPEWIRE_CORE");
|
||||
if (d.daemon_name == NULL)
|
||||
d.daemon_name = PW_DEFAULT_REMOTE;
|
||||
|
||||
while ((c = getopt_long(argc, argv, "hVn:", long_options, NULL)) != -1) {
|
||||
switch (c) {
|
||||
case 'h' :
|
||||
show_help(argv[0]);
|
||||
show_help(&d, argv[0]);
|
||||
return 0;
|
||||
case 'V' :
|
||||
fprintf(stdout, "%s\n"
|
||||
|
|
@ -89,8 +402,8 @@ int main(int argc, char *argv[])
|
|||
pw_get_library_version());
|
||||
return 0;
|
||||
case 'n' :
|
||||
daemon_name = optarg;
|
||||
fprintf(stdout, "set name %s\n", daemon_name);
|
||||
d.daemon_name = optarg;
|
||||
fprintf(stdout, "set name %s\n", d.daemon_name);
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
|
|
@ -98,47 +411,57 @@ int main(int argc, char *argv[])
|
|||
}
|
||||
|
||||
properties = pw_properties_new(
|
||||
PW_KEY_CORE_NAME, daemon_name,
|
||||
PW_KEY_CORE_NAME, d.daemon_name,
|
||||
PW_KEY_CONTEXT_PROFILE_MODULES, "none",
|
||||
PW_KEY_CORE_DAEMON, "true", NULL);
|
||||
|
||||
/* parse configuration */
|
||||
config = pw_daemon_config_new(properties);
|
||||
if (pw_daemon_config_load(config, &err) < 0) {
|
||||
pw_log_error("failed to parse config: %s", err);
|
||||
free(err);
|
||||
return -1;
|
||||
}
|
||||
if ((str = pw_properties_get(d.conf, "properties")) != NULL)
|
||||
pw_properties_update_string(properties, str, strlen(str));
|
||||
|
||||
|
||||
loop = pw_main_loop_new(&properties->dict);
|
||||
if (loop == NULL) {
|
||||
d.loop = pw_main_loop_new(&properties->dict);
|
||||
if (d.loop == NULL) {
|
||||
pw_log_error("failed to create main-loop: %m");
|
||||
return -1;
|
||||
}
|
||||
|
||||
pw_loop_add_signal(pw_main_loop_get_loop(loop), SIGINT, do_quit, loop);
|
||||
pw_loop_add_signal(pw_main_loop_get_loop(loop), SIGTERM, do_quit, loop);
|
||||
pw_loop_add_signal(pw_main_loop_get_loop(d.loop), SIGINT, do_quit, &d);
|
||||
pw_loop_add_signal(pw_main_loop_get_loop(d.loop), SIGTERM, do_quit, &d);
|
||||
|
||||
context = pw_context_new(pw_main_loop_get_loop(loop), properties, 0);
|
||||
if (context == NULL) {
|
||||
d.context = pw_context_new(pw_main_loop_get_loop(d.loop), properties, 0);
|
||||
if (d.context == NULL) {
|
||||
pw_log_error("failed to create context: %m");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((res = pw_daemon_config_run_commands(config, context)) < 0) {
|
||||
pw_log_error("failed to run config commands: %s", spa_strerror(res));
|
||||
pw_main_loop_quit(loop);
|
||||
return -1;
|
||||
if ((str = pw_properties_get(d.conf, "spa-libs")) != NULL)
|
||||
parse_spa_libs(&d, str);
|
||||
if ((str = pw_properties_get(d.conf, "modules")) != NULL) {
|
||||
if ((res = parse_modules(&d, str)) < 0) {
|
||||
pw_log_error("failed to load modules: %s", spa_strerror(res));
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
if ((str = pw_properties_get(d.conf, "objects")) != NULL) {
|
||||
if ((res = parse_objects(&d, str)) < 0) {
|
||||
pw_log_error("failed to load objects: %s", spa_strerror(res));
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
if ((str = pw_properties_get(d.conf, "exec")) != NULL) {
|
||||
if ((res = parse_exec(&d, str)) < 0) {
|
||||
pw_log_error("failed to exec: %s", spa_strerror(res));
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
pw_log_info("start main loop");
|
||||
pw_main_loop_run(loop);
|
||||
pw_main_loop_run(d.loop);
|
||||
pw_log_info("leave main loop");
|
||||
|
||||
pw_daemon_config_free(config);
|
||||
pw_context_destroy(context);
|
||||
pw_main_loop_destroy(loop);
|
||||
pw_properties_free(d.conf);
|
||||
pw_context_destroy(d.context);
|
||||
pw_main_loop_destroy(d.loop);
|
||||
pw_deinit();
|
||||
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -1,14 +1,7 @@
|
|||
pipewire_daemon_sources = [
|
||||
'command.c',
|
||||
'daemon-config.c',
|
||||
'main.c',
|
||||
]
|
||||
|
||||
pipewire_daemon_headers = [
|
||||
'command.h',
|
||||
'daemon-config.h',
|
||||
]
|
||||
|
||||
pipewire_c_args = [
|
||||
'-DHAVE_CONFIG_H',
|
||||
'-D_GNU_SOURCE',
|
||||
|
|
|
|||
|
|
@ -1,90 +1,101 @@
|
|||
#daemon config file for PipeWire version @VERSION@
|
||||
{
|
||||
properties = {
|
||||
## configure properties in the system
|
||||
#library.name.system = support/libspa-support
|
||||
#context.data-loop.library.name.system = support/libspa-support
|
||||
#link.max-buffers = 64
|
||||
link.max-buffers = 16 # version < 3 clients can't handle more
|
||||
#mem.allow-mlock = true
|
||||
#log.level = 2
|
||||
|
||||
## set-prop is used to configure properties in the system
|
||||
#
|
||||
#set-prop library.name.system support/libspa-support
|
||||
#set-prop context.data-loop.library.name.system support/libspa-support
|
||||
#set-prop link.max-buffers 64
|
||||
set-prop link.max-buffers 16 # version < 3 clients can't handle more
|
||||
#set-prop mem.allow-mlock true
|
||||
#set-prop log.level 2
|
||||
## Properties for the DSP configuration
|
||||
#default.clock.rate = 48000
|
||||
#default.clock.quantum = 1024
|
||||
#default.clock.min-quantum = 32
|
||||
#default.clock.max-quantum = 8192
|
||||
#default.video.width = 640
|
||||
#default.video.height = 480
|
||||
#default.video.rate.num = 25
|
||||
#default.video.rate.denom = 1
|
||||
}
|
||||
|
||||
## Properties for the DSP configuration
|
||||
#
|
||||
#set-prop default.clock.rate 48000
|
||||
#set-prop default.clock.quantum 1024
|
||||
#set-prop default.clock.min-quantum 32
|
||||
#set-prop default.clock.max-quantum 8192
|
||||
#set-prop default.video.width 640
|
||||
#set-prop default.video.height 480
|
||||
#set-prop default.video.rate.num 25
|
||||
#set-prop default.video.rate.denom 1
|
||||
spa-libs = {
|
||||
## <factory-name regex> = <library-name>
|
||||
#
|
||||
# used to find spa factory names. It maps an spa factory name
|
||||
# regular expression to a library name that should contain
|
||||
# that factory.
|
||||
#
|
||||
audio.convert* = audioconvert/libspa-audioconvert
|
||||
api.alsa.* = alsa/libspa-alsa
|
||||
api.v4l2.* = v4l2/libspa-v4l2
|
||||
api.libcamera.* = libcamera/libspa-libcamera
|
||||
api.bluez5.* = bluez5/libspa-bluez5
|
||||
api.vulkan.* = vulkan/libspa-vulkan
|
||||
api.jack.* = jack/libspa-jack
|
||||
support.* = support/libspa-support
|
||||
#videotestsrc = videotestsrc/libspa-videotestsrc
|
||||
#audiotestsrc = audiotestsrc/libspa-audiotestsrc
|
||||
}
|
||||
|
||||
## add-spa-lib <factory-name regex> <library-name>
|
||||
#
|
||||
# used to find spa factory names. It maps an spa factory name
|
||||
# regular expression to a library name that should contain
|
||||
# that factory.
|
||||
#
|
||||
add-spa-lib audio.convert* audioconvert/libspa-audioconvert
|
||||
add-spa-lib api.alsa.* alsa/libspa-alsa
|
||||
add-spa-lib api.v4l2.* v4l2/libspa-v4l2
|
||||
add-spa-lib api.libcamera.* libcamera/libspa-libcamera
|
||||
add-spa-lib api.bluez5.* bluez5/libspa-bluez5
|
||||
add-spa-lib api.vulkan.* vulkan/libspa-vulkan
|
||||
add-spa-lib api.jack.* jack/libspa-jack
|
||||
add-spa-lib support.* support/libspa-support
|
||||
#add-spa-lib videotestsrc videotestsrc/libspa-videotestsrc
|
||||
#add-spa-lib audiotestsrc audiotestsrc/libspa-audiotestsrc
|
||||
modules = {
|
||||
## <module-name> = { [args = "<key>=<value> ..."]
|
||||
# [flags = ifexists] }
|
||||
#
|
||||
# Loads a module with the given parameters. Normally failure is
|
||||
# fatal if the module is not found, unless -ifexists is given.
|
||||
#
|
||||
libpipewire-module-rtkit = { "#args" = "rt.prio=20 rt.time.soft=200000 rt.time.hard=200000" flags=ifexists }
|
||||
libpipewire-module-protocol-native = null
|
||||
libpipewire-module-profiler = null
|
||||
libpipewire-module-metadata = null
|
||||
libpipewire-module-spa-device-factory = null
|
||||
libpipewire-module-spa-node-factory = null
|
||||
libpipewire-module-client-node = null
|
||||
libpipewire-module-client-device = null
|
||||
libpipewire-module-portal = null
|
||||
libpipewire-module-access = { "#args" = "access.allowed=@media_session_path@ access.force=flatpak" }
|
||||
libpipewire-module-adapter = null
|
||||
libpipewire-module-link-factory = null
|
||||
libpipewire-module-session-manager = null
|
||||
}
|
||||
|
||||
## load-module [-ifexists] <module-name> [<key>=<value> ...]
|
||||
#
|
||||
# Loads a module with the given parameters. Normally failure is
|
||||
# fatal if the module is not found, unless -ifexists is given.
|
||||
#
|
||||
load-module libpipewire-module-rtkit # rt.prio=20 rt.time.soft=200000 rt.time.hard=200000
|
||||
load-module libpipewire-module-protocol-native
|
||||
load-module libpipewire-module-profiler
|
||||
load-module libpipewire-module-metadata
|
||||
load-module libpipewire-module-spa-device-factory
|
||||
load-module libpipewire-module-spa-node-factory
|
||||
load-module libpipewire-module-client-node
|
||||
load-module libpipewire-module-client-device
|
||||
load-module libpipewire-module-portal
|
||||
load-module libpipewire-module-access # access.allowed=@media_session_path@ access.force=flatpak
|
||||
load-module libpipewire-module-adapter
|
||||
load-module libpipewire-module-link-factory
|
||||
load-module libpipewire-module-session-manager
|
||||
objects = {
|
||||
## <factory-name> = { [args = "<key>=<value> ..."]
|
||||
# [flags = nofail] }
|
||||
#
|
||||
# Creates an object from a PipeWire factory with the given parameters.
|
||||
# If nofail is given, errors are ignored (and no object is created)
|
||||
#
|
||||
#spa-node-factory = { args = "factory.name=videotestsrc node.name=videotestsrc Spa:Pod:Object:Param:Props:patternType=1" }
|
||||
#spa-device-factory = { args = "factory.name=api.jack.device foo=bar" flags = nofail }
|
||||
#spa-device-factory = { args = "factory.name=api.alsa.enum.udev" }
|
||||
#spa-device-factory = { args = "factory.name=api.alsa.seq.bridge node.name=Internal-MIDI-Bridge" }
|
||||
#adapter = { args = "factory.name=audiotestsrc node.name=my-test" }
|
||||
#spa-node-factory = { args = "factory.name=api.vulkan.compute.source node.name=my-compute-source" }
|
||||
spa-node-factory = { args = "factory.name=support.node.driver node.name=Dummy priority.driver=8000" }
|
||||
}
|
||||
|
||||
## create-object [-nofail] <factory-name> [<key>=<value> ...]
|
||||
#
|
||||
# Creates an object from a PipeWire factory with the given parameters.
|
||||
# If -nofail is given, errors are ignored (and no object is created)
|
||||
#
|
||||
#create-object spa-node-factory factory.name=videotestsrc node.name=videotestsrc Spa:Pod:Object:Param:Props:patternType=1
|
||||
#create-object -nofail spa-device-factory factory.name=api.jack.device foo=bar
|
||||
#create-object spa-device-factory factory.name=api.alsa.enum.udev
|
||||
#create-object spa-device-factory factory.name=api.alsa.seq.bridge node.name=Internal-MIDI-Bridge
|
||||
#create-object adapter factory.name=audiotestsrc node.name=my-test
|
||||
#create-object spa-node-factory factory.name=api.vulkan.compute.source node.name=my-compute-source
|
||||
create-object spa-node-factory factory.name=support.node.driver node.name=Dummy priority.driver=8000
|
||||
|
||||
## exec <program-name>
|
||||
#
|
||||
# Execute the given program.
|
||||
#
|
||||
# Start the session manager. Run the session manager with -h for
|
||||
# options.
|
||||
#
|
||||
# The bluetooth module is disabled by default because it causes
|
||||
# conflicts with PulseAudio. If you disable PulseAudio or don't
|
||||
# load its bluetooth module, you can enable it here with -e bluez5
|
||||
#
|
||||
exec @media_session_path@
|
||||
#
|
||||
# You can optionally start the pulseaudio-server here as well
|
||||
# but it better to start it as a systemd service.
|
||||
# It can be interesting to start another daemon here that listens
|
||||
# on another address with the -a option (eg. -a tcp:4713)
|
||||
#
|
||||
#exec @pipewire_pulse_path@
|
||||
exec = {
|
||||
## <program-name> = { [args = "<arguments>"] }
|
||||
#
|
||||
# Execute the given program with arguments.
|
||||
#
|
||||
# Start the session manager. Run the session manager with -h for
|
||||
# options.
|
||||
#
|
||||
# The bluetooth module is disabled by default because it causes
|
||||
# conflicts with PulseAudio. If you disable PulseAudio or don't
|
||||
# load its bluetooth module, you can enable it here with -e bluez5
|
||||
#
|
||||
"@media_session_path@" = { args = ""}
|
||||
#
|
||||
# You can optionally start the pulseaudio-server here as well
|
||||
# but it better to start it as a systemd service.
|
||||
# It can be interesting to start another daemon here that listens
|
||||
# on another address with the -a option (eg. -a tcp:4713)
|
||||
#
|
||||
#"@pipewire_pulse_path@" = { "#args" = "-a tcp:4713" }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue