mirror of
				https://gitlab.freedesktop.org/pipewire/pipewire.git
				synced 2025-11-03 09:01:54 -05: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);
 | 
			
		||||
	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