mirror of
https://gitlab.freedesktop.org/pipewire/pipewire.git
synced 2025-10-31 22:25:38 -04:00
tools: add pw-config
Add a tool to debug how config files are loaded and merged.
This commit is contained in:
parent
543965a8c3
commit
d1aeb8144b
2 changed files with 229 additions and 0 deletions
|
|
@ -1,5 +1,6 @@
|
|||
tools_sources = [
|
||||
[ 'pw-mon', [ 'pw-mon.c' ] ],
|
||||
[ 'pw-config', [ 'pw-config.c' ] ],
|
||||
[ 'pw-dot', [ 'pw-dot.c' ] ],
|
||||
[ 'pw-dump', [ 'pw-dump.c' ] ],
|
||||
[ 'pw-profiler', [ 'pw-profiler.c' ] ],
|
||||
|
|
|
|||
228
src/tools/pw-config.c
Normal file
228
src/tools/pw-config.c
Normal file
|
|
@ -0,0 +1,228 @@
|
|||
/* PipeWire */
|
||||
/* SPDX-FileCopyrightText: Copyright © 2023 Wim Taymans */
|
||||
/* SPDX-License-Identifier: MIT */
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <getopt.h>
|
||||
#include <signal.h>
|
||||
#include <locale.h>
|
||||
|
||||
#include <spa/utils/result.h>
|
||||
#include <spa/utils/json.h>
|
||||
|
||||
#include "pipewire/pipewire.h"
|
||||
#include "pipewire/log.h"
|
||||
|
||||
#define DEFAULT_NAME "pipewire.conf"
|
||||
#define DEFAULT_PREFIX ""
|
||||
|
||||
struct data {
|
||||
const char *opt_name;
|
||||
const char *opt_prefix;
|
||||
const char *opt_cmd;
|
||||
bool opt_recurse;
|
||||
bool opt_newline;
|
||||
struct pw_properties *conf;
|
||||
struct pw_properties *assemble;
|
||||
int count;
|
||||
bool array;
|
||||
};
|
||||
|
||||
static void print_all_properties(struct data *d, struct pw_properties *props)
|
||||
{
|
||||
pw_properties_serialize_dict(stdout,
|
||||
&props->dict,
|
||||
(d->opt_newline ? PW_PROPERTIES_FLAG_NL : 0) |
|
||||
(d->opt_recurse ? PW_PROPERTIES_FLAG_RECURSE : 0) |
|
||||
(d->array ? PW_PROPERTIES_FLAG_ARRAY : 0) |
|
||||
PW_PROPERTIES_FLAG_ENCLOSE);
|
||||
fprintf(stdout, "\n");
|
||||
}
|
||||
|
||||
static void list_paths(struct data *d)
|
||||
{
|
||||
const struct spa_dict_item *it;
|
||||
|
||||
spa_dict_for_each(it, &d->conf->dict) {
|
||||
if (spa_strstartswith(it->key, "config.path")) {
|
||||
pw_properties_set(d->assemble, it->key, it->value);
|
||||
}
|
||||
if (spa_strstartswith(it->key, "override.") &&
|
||||
spa_strendswith(it->key, ".config.path")) {
|
||||
pw_properties_set(d->assemble, it->key, it->value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int do_merge_section(void *data, const char *location, const char *section,
|
||||
const char *str, size_t len)
|
||||
{
|
||||
struct data *d = data;
|
||||
struct spa_json it[2];
|
||||
int l;
|
||||
const char *value;
|
||||
|
||||
spa_json_init(&it[0], str, len);
|
||||
if ((l = spa_json_next(&it[0], &value)) <= 0)
|
||||
return 0;
|
||||
|
||||
if (spa_json_is_array(value, l)) {
|
||||
char key[128];
|
||||
|
||||
spa_json_enter(&it[0], &it[1]);
|
||||
while ((l = spa_json_next(&it[1], &value)) > 0) {
|
||||
if (spa_json_is_container(value, l))
|
||||
l = spa_json_container_len(&it[1], value, l);
|
||||
|
||||
snprintf(key, sizeof(key), "%d-%s", d->count++, location);
|
||||
pw_properties_setf(d->assemble, key, "%.*s", l, value);
|
||||
}
|
||||
d->array = true;
|
||||
}
|
||||
else if (spa_json_is_object(value, l)) {
|
||||
pw_properties_update_string(d->assemble, str, len);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int do_list_section(void *data, const char *location, const char *section,
|
||||
const char *str, size_t len)
|
||||
{
|
||||
struct data *d = data;
|
||||
char key[128];
|
||||
snprintf(key, sizeof(key), "%d-%s", d->count++, location);
|
||||
pw_properties_setf(d->assemble, key, "%.*s", (int)len, str);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void section_for_each(struct data *d, const char *section,
|
||||
int (*callback) (void *data, const char *location, const char *section,
|
||||
const char *str, size_t len))
|
||||
{
|
||||
const char *str;
|
||||
char key[128];
|
||||
|
||||
pw_conf_section_for_each(&d->conf->dict, section, callback, d);
|
||||
str = pw_properties_get(d->assemble, "config.ext");
|
||||
if (str != NULL) {
|
||||
snprintf(key, sizeof(key), "%s.%s", section, str);
|
||||
pw_conf_section_for_each(&d->conf->dict, key, callback, d);
|
||||
}
|
||||
}
|
||||
|
||||
static void show_help(const char *name, bool error)
|
||||
{
|
||||
fprintf(error ? stderr : stdout, "%1$s : PipeWire config manager.\n"
|
||||
"Usage:\n"
|
||||
" %1$s [options] paths List config paths (default action)\n"
|
||||
" %1$s [options] list [SECTION] List config section\n"
|
||||
" %1$s [options] merge SECTION Merge a config section\n\n"
|
||||
"Options:\n"
|
||||
" -h, --help Show this help\n"
|
||||
" --version Show version\n"
|
||||
" -n, --name Config Name (default '%2$s')\n"
|
||||
" -p, --prefix Config Prefix (default '%3$s')\n"
|
||||
" -L, --no-newline Omit newline after values\n"
|
||||
" -r, --recurse Dump values recursively\n",
|
||||
name, DEFAULT_NAME, DEFAULT_PREFIX);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
struct data d = { 0, };
|
||||
struct pw_properties *props = NULL;
|
||||
int res = 0, c;
|
||||
static const struct option long_options[] = {
|
||||
{ "help", no_argument, NULL, 'h' },
|
||||
{ "version", no_argument, NULL, 'V' },
|
||||
{ "name", required_argument, NULL, 'n' },
|
||||
{ "prefix", required_argument, NULL, 'p' },
|
||||
{ "no-newline", no_argument, NULL, 'L' },
|
||||
{ "recurse", no_argument, NULL, 'r' },
|
||||
{ NULL, 0, NULL, 0}
|
||||
};
|
||||
|
||||
d.opt_name = DEFAULT_NAME;
|
||||
d.opt_prefix = NULL;
|
||||
d.opt_recurse = false;
|
||||
d.opt_newline = true;
|
||||
d.opt_cmd = "paths";
|
||||
|
||||
pw_init(&argc, &argv);
|
||||
|
||||
while ((c = getopt_long(argc, argv, "hVn:p:Lr", long_options, NULL)) != -1) {
|
||||
switch (c) {
|
||||
case 'h':
|
||||
show_help(argv[0], false);
|
||||
return 0;
|
||||
case 'V':
|
||||
printf("%s\n"
|
||||
"Compiled with libpipewire %s\n"
|
||||
"Linked with libpipewire %s\n",
|
||||
argv[0],
|
||||
pw_get_headers_version(),
|
||||
pw_get_library_version());
|
||||
return 0;
|
||||
case 'n':
|
||||
d.opt_name = optarg;
|
||||
break;
|
||||
case 'p':
|
||||
d.opt_prefix = optarg;
|
||||
break;
|
||||
case 'L':
|
||||
d.opt_newline = false;
|
||||
break;
|
||||
case 'r':
|
||||
d.opt_recurse = true;
|
||||
break;
|
||||
default:
|
||||
show_help(argv[0], true);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (optind < argc)
|
||||
d.opt_cmd = argv[optind++];
|
||||
|
||||
props = pw_properties_new(
|
||||
PW_KEY_CONFIG_NAME, d.opt_name,
|
||||
PW_KEY_CONFIG_PREFIX, d.opt_prefix,
|
||||
NULL);
|
||||
|
||||
d.conf = pw_properties_new(NULL, NULL);
|
||||
if ((res = pw_conf_load_conf_for_context (props, d.conf)) < 0) {
|
||||
fprintf(stderr, "error loading config: %s\n", spa_strerror(res));
|
||||
goto done;
|
||||
}
|
||||
|
||||
d.assemble = pw_properties_new(NULL, NULL);
|
||||
|
||||
if (spa_streq(d.opt_cmd, "paths")) {
|
||||
list_paths(&d);
|
||||
}
|
||||
else if (spa_streq(d.opt_cmd, "list")) {
|
||||
if (optind == argc) {
|
||||
pw_properties_update(d.assemble, &d.conf->dict);
|
||||
} else {
|
||||
section_for_each(&d, argv[optind++], do_list_section);
|
||||
}
|
||||
}
|
||||
else if (spa_streq(d.opt_cmd, "merge")) {
|
||||
if (optind == argc) {
|
||||
fprintf(stderr, "%s requires a section\n", d.opt_cmd);
|
||||
res = -EINVAL;
|
||||
goto done;
|
||||
}
|
||||
section_for_each(&d, argv[optind++], do_merge_section);
|
||||
}
|
||||
print_all_properties(&d, d.assemble);
|
||||
|
||||
done:
|
||||
pw_properties_free(d.conf);
|
||||
pw_properties_free(d.assemble);
|
||||
pw_properties_free(props);
|
||||
|
||||
pw_deinit();
|
||||
return res;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue