From fc90a4e48a7072e17eeeb44b81efd7f7cc835e8a Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Thu, 11 Feb 2021 17:54:21 +0100 Subject: [PATCH] Implement config/state file handling Make methods to load_config and load/save state. For now the config and state directories are the same but it might not be. Implement the search path for all config/state files as: $XDG_CONFIG_HOME/[$prefix]/$name $HOME/.config/[$prefix]/$name $PIPEWIRE_CONFIG_DIR/pipewire/[$prefix]/$name /etc/pipewire/[$prefix]/$name Make some config files for jack and RT clients. Make pw-cat use the client-rt config. Use core state and config management in media-session. Move all session manager state and config files to the build dir and set the PIPEWIRE_CONFIG_DIR to this build dir. --- Makefile.in | 2 +- pipewire-jack/src/pipewire-jack.c | 6 +- pw-uninstalled.sh | 2 +- src/daemon/client-rt.conf.in | 63 ++++++ src/daemon/jack.conf.in | 52 +++++ src/daemon/media-session.d/meson.build | 21 ++ src/daemon/meson.build | 36 ++-- src/examples/media-session/media-session.c | 171 +--------------- src/pipewire/conf.c | 217 +++++++++++++++++++-- src/pipewire/conf.h | 4 +- src/pipewire/context.c | 18 +- src/tests/test-context.c | 2 +- src/tools/pw-cat.c | 2 +- 13 files changed, 380 insertions(+), 216 deletions(-) create mode 100644 src/daemon/client-rt.conf.in create mode 100644 src/daemon/jack.conf.in create mode 100644 src/daemon/media-session.d/meson.build diff --git a/Makefile.in b/Makefile.in index 695e7704e..522c0a295 100644 --- a/Makefile.in +++ b/Makefile.in @@ -14,7 +14,7 @@ run: all SPA_PLUGIN_DIR=@BUILD_ROOT@/spa/plugins \ PIPEWIRE_MODULE_DIR=@BUILD_ROOT@/src/modules/ \ PATH=@BUILD_ROOT@/src/examples:$(PATH) \ - PIPEWIRE_CONFIG_DIR=@SOURCE_ROOT@/src/daemon/ \ + PIPEWIRE_CONFIG_DIR=@BUILD_ROOT@/src/daemon/ \ ACP_PATHS_DIR=@SOURCE_ROOT@/spa/plugins/alsa/mixer/paths \ ACP_PROFILES_DIR=@SOURCE_ROOT@/spa/plugins/alsa/mixer/profile-sets \ $(DBG) @BUILD_ROOT@/src/daemon/pipewire diff --git a/pipewire-jack/src/pipewire-jack.c b/pipewire-jack/src/pipewire-jack.c index a1b0bc552..e7c467032 100644 --- a/pipewire-jack/src/pipewire-jack.c +++ b/pipewire-jack/src/pipewire-jack.c @@ -2410,15 +2410,11 @@ jack_client_t * jack_client_open (const char *client_name, if (client->props == NULL) goto no_props; - if (pw_properties_get(client->props, PW_KEY_CONTEXT_PROFILE_MODULES) == NULL) { - if ((str = getenv("PIPEWIRE_PROFILE_MODULES")) == NULL) - str = "default,rtkit"; - pw_properties_set(client->props, PW_KEY_CONTEXT_PROFILE_MODULES, str); - } pw_properties_set(client->props, "loop.cancel", "true"); pw_properties_set(client->props, PW_KEY_REMOTE_NAME, client->server_name); pw_properties_set(client->props, PW_KEY_CLIENT_NAME, client_name); pw_properties_set(client->props, PW_KEY_CLIENT_API, "jack"); + pw_properties_set(client->props, PW_KEY_CONFIG_NAME, "jack.conf"); client->node_id = SPA_ID_INVALID; strncpy(client->name, client_name, JACK_CLIENT_NAME_SIZE); diff --git a/pw-uninstalled.sh b/pw-uninstalled.sh index d7717d009..1236cdc52 100755 --- a/pw-uninstalled.sh +++ b/pw-uninstalled.sh @@ -32,7 +32,7 @@ if [ ! -d ${BUILDDIR} ]; then fi # the config file read by the daemon -export PIPEWIRE_CONFIG_DIR="src/daemon/" +export PIPEWIRE_CONFIG_DIR="${BUILDDIR}/src/daemon" # the directory with SPA plugins export SPA_PLUGIN_DIR="${BUILDDIR}/spa/plugins" # the directory with pipewire modules diff --git a/src/daemon/client-rt.conf.in b/src/daemon/client-rt.conf.in new file mode 100644 index 000000000..05f9f6344 --- /dev/null +++ b/src/daemon/client-rt.conf.in @@ -0,0 +1,63 @@ +# Daemon config file for PipeWire RT clients version @VERSION@ # + +properties = { + ## Configure properties in the system. + #mem.warn-mlock = false + #mem.allow-mlock = true + #mem.mlock-all = false + #log.level = 2 +} + +spa-libs = { + ## = + # + # 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 + support.* = support/libspa-support +} + +modules = { + ## = { [args = { = ... }] + # [flags = [ [ifexists] [nofail] ]} + # + # Loads a module with the given parameters. + # If ifexists is given, the module is ignored when it is not found. + # If nofail is given, module initialization failures are ignored. + # + # Uses RTKit to boost the data thread priority. + libpipewire-module-rtkit = { + args = { + #nice.level = -11 + #rt.prio = 20 + #rt.time.soft = 200000 + #rt.time.hard = 200000 + } + flags = [ ifexists nofail ] + } + + # The native communication protocol. + libpipewire-module-protocol-native = null + + # Allows creating nodes that run in the context of the + # client. Is used by all clients that want to provide + # data to PipeWire. + libpipewire-module-client-node = null + + # Allows creating devices that run in the context of the + # client. Is used by the session manager. + libpipewire-module-client-device = null + + # Makes a factory for wrapping nodes in an adapter with a + # converter and resampler. + libpipewire-module-adapter = null + + # Allows applications to create metadata objects. It creates + # a factory for Metadata objects. + libpipewire-module-metadata = null + + # Provides factories to make session manager objects. + libpipewire-module-session-manager = null +} diff --git a/src/daemon/jack.conf.in b/src/daemon/jack.conf.in new file mode 100644 index 000000000..d40ff142c --- /dev/null +++ b/src/daemon/jack.conf.in @@ -0,0 +1,52 @@ +# Daemon config file for PipeWire JACK clients version @VERSION@ # + +properties = { + ## Configure properties in the system. + #mem.warn-mlock = false + #mem.allow-mlock = true + #mem.mlock-all = false + #log.level = 2 +} + +spa-libs = { + ## = + # + # Used to find spa factory names. It maps an spa factory name + # regular expression to a library name that should contain + # that factory. + # + support.* = support/libspa-support +} + +modules = { + ## = { [args = { = ... }] + # [flags = [ [ifexists] [nofail] ]} + # + # Loads a module with the given parameters. + # If ifexists is given, the module is ignored when it is not found. + # If nofail is given, module initialization failures are ignored. + # + # + # Uses RTKit to boost the data thread priority. + libpipewire-module-rtkit = { + args = { + #nice.level = -11 + #rt.prio = 20 + #rt.time.soft = 200000 + #rt.time.hard = 200000 + } + flags = [ ifexists nofail ] + } + + # The native communication protocol. + libpipewire-module-protocol-native = null + + # Allows creating nodes that run in the context of the + # client. Is used by all clients that want to provide + # data to PipeWire. + libpipewire-module-client-node = null + + # Allows applications to create metadata objects. It creates + # a factory for Metadata objects. + libpipewire-module-metadata = null +} diff --git a/src/daemon/media-session.d/meson.build b/src/daemon/media-session.d/meson.build new file mode 100644 index 000000000..ac7fb20a0 --- /dev/null +++ b/src/daemon/media-session.d/meson.build @@ -0,0 +1,21 @@ +conf_files = [ + [ 'bluez-monitor.conf', 'bluez-monitor.conf' ], + [ 'v4l2-monitor.conf', 'v4l2-monitor.conf' ], + [ 'media-session.conf', 'media-session.conf' ], + [ 'alsa-monitor.conf', 'alsa-monitor.conf' ], + [ 'with-jack', 'with-jack' ], + [ 'with-pulseaudio', 'with-pulseaudio' ], +] + +foreach c : conf_files + configure_file(input : c.get(0), + output : c.get(1), + configuration : conf_config, + install_dir : join_paths(conf_install_dir, 'media-session.d')) +endforeach + +install_data( + sources : [ + 'with-jack', + 'with-pulseaudio' ], + install_dir : join_paths(conf_install_dir, 'media-session.d')) diff --git a/src/daemon/meson.build b/src/daemon/meson.build index fb5f953eb..85e3efe87 100644 --- a/src/daemon/meson.build +++ b/src/daemon/meson.build @@ -14,36 +14,27 @@ conf_config.set('media_session_path', join_paths(pipewire_bindir, 'pipewire-medi conf_config.set('pipewire_pulse_path', join_paths(pipewire_bindir, 'pipewire-pulse')) conf_install_dir = join_paths(get_option('sysconfdir'), 'pipewire') -install_data( - sources : [ - 'media-session.d/with-jack', - 'media-session.d/with-pulseaudio', - 'media-session.d/bluez-monitor.conf', - 'media-session.d/v4l2-monitor.conf', - 'media-session.d/media-session.conf', - 'media-session.d/alsa-monitor.conf' ], - install_dir : join_paths(conf_install_dir, 'media-session.d')) - conf_config_uninstalled = conf_config conf_config_uninstalled.set('media_session_path', join_paths(meson.build_root(), 'src', 'examples', 'pipewire-media-session')) conf_config_uninstalled.set('pipewire_pulse_path', join_paths(meson.build_root(), 'src', 'daemon', 'pipewire-pulse')) -configure_file(input : 'pipewire.conf.in', - output : 'pipewire.conf', - configuration : conf_config, - install_dir : conf_install_dir) -configure_file(input : 'client.conf.in', - output : 'client.conf', - configuration : conf_config, - install_dir : conf_install_dir) +conf_files = [ + [ 'pipewire.conf.in', 'pipewire.conf' ], + [ 'client.conf.in', 'client.conf' ], + [ 'client-rt.conf.in', 'client-rt.conf' ], + [ 'jack.conf.in', 'jack.conf' ], + [ 'pipewire-pulse.conf.in', 'pipewire-pulse.conf' ] +] -configure_file(input : 'pipewire-pulse.conf.in', - output : 'pipewire-pulse.conf', - configuration : conf_config, - install_dir : conf_install_dir) +foreach c : conf_files + configure_file(input : c.get(0), + output : c.get(1), + configuration : conf_config, + install_dir : conf_install_dir) +endforeach configure_file(input : 'pipewire.conf.in', output : 'pipewire-uninstalled.conf', @@ -81,6 +72,7 @@ executable('pipewire-pulse', # ) #endif +subdir('media-session.d') if get_option('systemd') and systemd.found() subdir('systemd') endif diff --git a/src/examples/media-session/media-session.c b/src/examples/media-session/media-session.c index 8598fd673..37256a523 100644 --- a/src/examples/media-session/media-session.c +++ b/src/examples/media-session/media-session.c @@ -150,9 +150,6 @@ struct impl { int rescan_seq; int last_seq; - int state_dir_fd; - char state_dir[PATH_MAX]; - unsigned int scanning:1; unsigned int rescan_pending:1; }; @@ -1786,171 +1783,19 @@ int sm_media_session_remove_links(struct sm_media_session *sess, int sm_media_session_load_conf(struct sm_media_session *sess, const char *name, struct pw_properties *conf) { - const char *dir; - char path[PATH_MAX]; - int count, fd; - struct stat sbuf; - char *data; - - if ((count = sm_media_session_load_state(sess, name, NULL, conf)) >= 0) - return count; - - if ((dir = getenv("PIPEWIRE_CONFIG_DIR")) == NULL) - dir = PIPEWIRE_CONFIG_DIR; - if (dir == NULL) - return -ENOENT; - - snprintf(path, sizeof(path), "%s/media-session.d/%s", dir, name); - if ((fd = open(path, O_CLOEXEC | O_RDONLY)) < 0) { - pw_log_warn(NAME" %p: error loading config '%s': %m", sess, path); - return -errno; - } - - pw_log_info(NAME" %p: loading config '%s'", sess, 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); - - count = pw_properties_update_string(conf, data, sbuf.st_size); - munmap(data, sbuf.st_size); - - return count; - -error_close: - pw_log_debug("can't read file %s: %m", path); - close(fd); - return -errno; -} - -static int state_dir(struct sm_media_session *sess) -{ - struct impl *impl = SPA_CONTAINER_OF(sess, struct impl, this); - const char *home_dir; - int res; - - if (impl->state_dir_fd != -1) - return impl->state_dir_fd; - - home_dir = getenv("XDG_CONFIG_HOME"); - if (home_dir != NULL) - snprintf(impl->state_dir, sizeof(impl->state_dir), - "%s/pipewire-media-session/", home_dir); - else { - home_dir = getenv("HOME"); - if (home_dir == NULL) - home_dir = getenv("USERPROFILE"); - if (home_dir == NULL) { - struct passwd pwd, *result = NULL; - char buffer[4096]; - if (getpwuid_r(getuid(), &pwd, buffer, sizeof(buffer), &result) == 0) - home_dir = result ? result->pw_dir : NULL; - } - if (home_dir == NULL) { - pw_log_error("Can't determine home directory"); - return -ENOTSUP; - } - snprintf(impl->state_dir, sizeof(impl->state_dir), - "%s/.config/pipewire-media-session/", home_dir); - } - -#ifndef O_PATH -#define O_PATH 0 -#endif - - if ((res = open(impl->state_dir, O_CLOEXEC | O_DIRECTORY | O_PATH)) < 0) { - if (errno == ENOENT) { - pw_log_info("creating state directory %s", impl->state_dir); - if (mkdir(impl->state_dir, 0700) < 0) { - pw_log_info("Can't create state directory %s: %m", impl->state_dir); - return -errno; - } - } else { - pw_log_error("Can't open state directory %s: %m", impl->state_dir); - return -errno; - } - if ((res = open(impl->state_dir, O_CLOEXEC | O_DIRECTORY | O_PATH)) < 0) { - pw_log_error("Can't open state directory %s: %m", impl->state_dir); - return -EINVAL; - } - } - impl->state_dir_fd = res; - return res; + return pw_conf_load_conf(SESSION_PREFIX, name, conf); } int sm_media_session_load_state(struct sm_media_session *sess, const char *name, const char *prefix, struct pw_properties *props) { - struct impl *impl = SPA_CONTAINER_OF(sess, struct impl, this); - int count, sfd, fd; - struct stat sbuf; - void *data; - - if ((sfd = state_dir(sess)) < 0) - return sfd; - - if ((fd = openat(sfd, name, O_CLOEXEC | O_RDONLY)) < 0) { - pw_log_debug("can't open file %s%s: %m", impl->state_dir, name); - return -errno; - } - pw_log_info(NAME" %p: loading state '%s%s'", sess, impl->state_dir, name); - 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); - - count = pw_properties_update_string(props, data, sbuf.st_size); - munmap(data, sbuf.st_size); - - return count; - -error_close: - pw_log_debug("can't read file %s: %m", name); - close(fd); - return -errno; + return pw_conf_load_state(SESSION_PREFIX, name, props); } int sm_media_session_save_state(struct sm_media_session *sess, const char *name, const char *prefix, const struct pw_properties *props) { - const struct spa_dict_item *it; - char *tmp_name; - int sfd, fd; - FILE *f; - - pw_log_info(NAME" %p: saving state '%s'", sess, name); - if ((sfd = state_dir(sess)) < 0) - return sfd; - - tmp_name = alloca(strlen(name)+5); - sprintf(tmp_name, "%s.tmp", name); - if ((fd = openat(sfd, tmp_name, O_CLOEXEC | O_CREAT | O_WRONLY | O_TRUNC, 0700)) < 0) { - pw_log_error("can't open file '%s': %m", tmp_name); - return -errno; - } - - f = fdopen(fd, "w"); - fprintf(f, "{ \n"); - spa_dict_for_each(it, &props->dict) { - char key[1024]; - if (prefix != NULL && strstr(it->key, prefix) != it->key) - continue; - - if (spa_json_encode_string(key, sizeof(key)-1, it->key) >= (int)sizeof(key)-1) - continue; - - fprintf(f, " %s: %s\n", key, it->value); - } - fprintf(f, "}\n"); - fclose(f); - - if (renameat(sfd, tmp_name, sfd, name) < 0) { - pw_log_error("can't rename temp file '%s': %m", tmp_name); - return -errno; - } - return 0; + return pw_conf_save_state(SESSION_PREFIX, name, props); } static void monitor_core_done(void *data, uint32_t id, int seq) @@ -2170,7 +2015,7 @@ again: add = true; } else { snprintf(check_path, sizeof(check_path), - "%s/media-session.d/%s", dir, key); + "%s/"SESSION_PREFIX"/%s", dir, key); add = (stat(check_path, &statbuf) == 0); } if (add) { @@ -2259,7 +2104,6 @@ int main(int argc, char *argv[]) pw_init(&argc, &argv); - impl.state_dir_fd = -1; impl.this.props = pw_properties_new( PW_KEY_CONFIG_PREFIX, SESSION_PREFIX, PW_KEY_CONFIG_NAME, SESSION_CONF, @@ -2270,14 +2114,14 @@ int main(int argc, char *argv[]) if ((impl.conf = pw_properties_new(NULL, NULL)) == NULL) return -1; - pw_conf_load(SESSION_PREFIX, SESSION_CONF, impl.conf); + pw_conf_load_conf(SESSION_PREFIX, SESSION_CONF, impl.conf); if ((str = pw_properties_get(impl.conf, "properties")) != NULL) pw_properties_update_string(impl.this.props, str, strlen(str)); if ((impl.modules = pw_properties_new("default", "true", NULL)) == NULL) return -1; - if ((str = pw_properties_get(impl.conf, "session-modules")) != NULL) + if ((str = pw_properties_get(impl.conf, "session.modules")) != NULL) collect_modules(&impl, str); while ((c = getopt_long(argc, argv, "hV", long_options, NULL)) != -1) { @@ -2368,9 +2212,6 @@ exit: pw_properties_free(impl.conf); pw_properties_free(impl.modules); - if (impl.state_dir_fd != -1) - close(impl.state_dir_fd); - pw_deinit(); return res; diff --git a/src/pipewire/conf.c b/src/pipewire/conf.c index fb9420946..d9dd9d671 100644 --- a/src/pipewire/conf.c +++ b/src/pipewire/conf.c @@ -22,6 +22,8 @@ * DEALINGS IN THE SOFTWARE. */ +#include "config.h" + #include #include #include @@ -31,38 +33,205 @@ #include #include #include +#if HAVE_PWD_H +#include +#endif #include #include #include -#include "config.h" - #define NAME "config" -SPA_EXPORT -int pw_conf_load(const char *prefix, const char *name, struct pw_properties *conf) +static int make_path(char *path, int size, const char *paths[]) +{ + int i, len; + char *p = path; + for (i = 0; paths[i] != NULL; i++) { + len = snprintf(p, size, "%s%s", i == 0 ? "" : "/", paths[i]); + if (len < 0) + return -errno; + if (len >= size) + return -ENOSPC; + p += len; + size -= len; + } + return 0; +} + +static int get_read_path(char *path, size_t size, const char *prefix, const char *name) { - const char *path; - char filename[PATH_MAX], *data; - struct stat sbuf; - int fd; const char *dir; + char buffer[4096]; + + if (prefix[0] == '/') { + const char *paths[] = { prefix, name, NULL }; + if (make_path(path, size, paths) == 0 && + access(path, R_OK) == 0) + return 1; + return -ENOENT; + } + + dir = getenv("XDG_CONFIG_HOME"); + if (dir != NULL) { + const char *paths[] = { dir, "pipewire", prefix, name, NULL }; + if (make_path(path, size, paths) == 0 && + access(path, R_OK) == 0) + return 1; + } + dir = getenv("HOME"); + if (dir == NULL) { + struct passwd pwd, *result = NULL; + if (getpwuid_r(getuid(), &pwd, buffer, sizeof(buffer), &result) == 0) + dir = result ? result->pw_dir : NULL; + } + if (dir != NULL) { + const char *paths[] = { dir, ".config", "pipewire", prefix, name, NULL }; + if (make_path(path, size, paths) == 0 && + access(path, R_OK) == 0) + return 1; + } dir = getenv("PIPEWIRE_CONFIG_DIR"); if (dir == NULL) dir = PIPEWIRE_CONFIG_DIR; - if (dir == NULL) + if (dir != NULL) { + const char *paths[] = { dir, prefix, name, NULL }; + if (make_path(path, size, paths) == 0 && + access(path, R_OK) == 0) + return 1; + } + return 0; +} + +static int ensure_path(char *path, int size, const char *paths[]) +{ + int i, len, res; + char *p = path; + + for (i = 0; paths[i] != NULL; i++) { + len = snprintf(p, size, "%s/", paths[i]); + if (len < 0) + return -errno; + if (len >= size) + return -ENOSPC; + + p += len; + size -= len; + + if ((res = access(path, R_OK | W_OK | X_OK)) < 0) { + if (errno != ENOENT) + return -errno; + if ((res = mkdir(path, 0700)) < 0) { + pw_log_info("Can't create directory %s: %m", path); + return -errno; + } + if ((res = access(path, R_OK | W_OK | X_OK)) < 0) + return -errno; + + pw_log_info("created directory %s", path); + } + } + return 0; +} + +static int open_write_dir(char *path, int size, const char *prefix) +{ + const char *dir; + char buffer[4096]; + int res; + + if (prefix != NULL && prefix[0] == '/') { + const char *paths[] = { prefix, NULL }; + if (ensure_path(path, size, paths) == 0) + goto found; + } + dir = getenv("XDG_CONFIG_HOME"); + if (dir != NULL) { + const char *paths[] = { dir, "pipewire", prefix, NULL }; + if (ensure_path(path, size, paths) == 0) + goto found; + } + dir = getenv("HOME"); + if (dir == NULL) { + struct passwd pwd, *result = NULL; + if (getpwuid_r(getuid(), &pwd, buffer, sizeof(buffer), &result) == 0) + dir = result ? result->pw_dir : NULL; + } + if (dir != NULL) { + const char *paths[] = { dir, ".config", "pipewire", prefix, NULL }; + if (ensure_path(path, size, paths) == 0) + goto found; + } + return -ENOENT; +found: + if ((res = open(path, O_CLOEXEC | O_DIRECTORY | O_PATH)) < 0) { + pw_log_error("Can't open state directory %s: %m", path); + return -errno; + } + return res; +} + +SPA_EXPORT +int pw_conf_save_state(const char *prefix, const char *name, struct pw_properties *conf) +{ + const struct spa_dict_item *it; + char path[PATH_MAX]; + char *tmp_name; + int res, sfd, fd; + FILE *f; + + if ((sfd = open_write_dir(path, sizeof(path), prefix)) < 0) + return sfd; + + tmp_name = alloca(strlen(name)+5); + sprintf(tmp_name, "%s.tmp", name); + if ((fd = openat(sfd, tmp_name, O_CLOEXEC | O_CREAT | O_WRONLY | O_TRUNC, 0700)) < 0) { + pw_log_error("can't open file '%s': %m", tmp_name); + res = -errno; + goto error; + } + + f = fdopen(fd, "w"); + fprintf(f, "{ \n"); + spa_dict_for_each(it, &conf->dict) { + char key[1024]; + + if (spa_json_encode_string(key, sizeof(key)-1, it->key) >= (int)sizeof(key)-1) + continue; + + fprintf(f, " %s: %s\n", key, it->value); + } + fprintf(f, "}\n"); + fclose(f); + + if (renameat(sfd, tmp_name, sfd, name) < 0) { + pw_log_error("can't rename temp file '%s': %m", tmp_name); + res = -errno; + goto error; + } + res = 0; + pw_log_info(NAME" %p: saved state '%s%s'", conf, path, name); +error: + close(sfd); + return res; +} + +static int conf_load(const char *prefix, const char *name, struct pw_properties *conf) +{ + char path[PATH_MAX], *data; + struct stat sbuf; + int fd; + + if (prefix == NULL) { + prefix = name; + name = NULL; + } + + if (get_read_path(path, sizeof(path), prefix, name) == 0) { + pw_log_debug(NAME" %p: can't load config '%s': %m", conf, path); return -ENOENT; - - if (prefix) - snprintf(filename, sizeof(filename), "%s/%s/%s", - dir, prefix, name); - else - snprintf(filename, sizeof(filename), "%s/%s", - dir, name); - path = filename; - + } if ((fd = open(path, O_CLOEXEC | O_RDONLY)) < 0) { pw_log_warn(NAME" %p: error loading config '%s': %m", conf, path); return -errno; @@ -85,6 +254,18 @@ error_close: return -errno; } +SPA_EXPORT +int pw_conf_load_conf(const char *prefix, const char *name, struct pw_properties *conf) +{ + return conf_load(prefix, name, conf); +} + +SPA_EXPORT +int pw_conf_load_state(const char *prefix, const char *name, struct pw_properties *conf) +{ + return conf_load(prefix, name, conf); +} + static int parse_spa_libs(struct pw_context *context, const char *str) { struct spa_json it[2]; diff --git a/src/pipewire/conf.h b/src/pipewire/conf.h index e7f88ee7c..2aaf75ac5 100644 --- a/src/pipewire/conf.h +++ b/src/pipewire/conf.h @@ -24,7 +24,9 @@ #include -int pw_conf_load(const char *prefix, const char *name, struct pw_properties *conf); +int pw_conf_load_conf(const char *prefix, const char *name, struct pw_properties *conf); +int pw_conf_load_state(const char *prefix, const char *name, struct pw_properties *conf); +int pw_conf_save_state(const char *prefix, const char *name, const struct pw_properties *conf); int pw_context_parse_conf_section(struct pw_context *context, struct pw_properties *conf, const char *section); diff --git a/src/pipewire/context.c b/src/pipewire/context.c index 0b2e57064..09626c096 100644 --- a/src/pipewire/context.c +++ b/src/pipewire/context.c @@ -212,7 +212,23 @@ struct pw_context *pw_context_new(struct pw_loop *main_loop, res = -errno; goto error_free; } - pw_conf_load(conf_prefix, conf_name, conf); + if (strcmp(conf_name, "null") != 0 && + (res = pw_conf_load_conf(conf_prefix, conf_name, conf)) < 0) { + if (conf_prefix == NULL && strcmp(conf_name, "client.conf") == 0) { + pw_log_error(NAME" %p: can't load config %s: %s", + this, conf_name, spa_strerror(res)); + goto error_free; + } else { + pw_log_warn(NAME" %p: can't load config %s: %s. Using client.conf fallback", + this, conf_name, spa_strerror(res)); + } + conf_prefix = NULL; + if ((res = pw_conf_load_conf(NULL, "client.conf", conf)) < 0) { + pw_log_error(NAME" %p: can't load client.conf config: %s", + this, spa_strerror(res)); + goto error_free; + } + } this->conf = conf; if ((str = pw_properties_get(conf, "properties")) != NULL) diff --git a/src/tests/test-context.c b/src/tests/test-context.c index 621103f80..6691c6081 100644 --- a/src/tests/test-context.c +++ b/src/tests/test-context.c @@ -126,7 +126,7 @@ static void test_create(void) context = pw_context_new(pw_main_loop_get_loop(loop), pw_properties_new( - PW_KEY_CONTEXT_PROFILE_MODULES, "none", + PW_KEY_CONFIG_NAME, "null", NULL), 12); spa_assert(context != NULL); pw_context_add_listener(context, &listener, &context_events, context); diff --git a/src/tools/pw-cat.c b/src/tools/pw-cat.c index ad47f9af7..821e9c5fd 100644 --- a/src/tools/pw-cat.c +++ b/src/tools/pw-cat.c @@ -1553,7 +1553,7 @@ int main(int argc, char *argv[]) data.context = pw_context_new(l, pw_properties_new( - PW_KEY_CONTEXT_PROFILE_MODULES, "default,rtkit", + PW_KEY_CONFIG_NAME, "client-rt.conf", NULL), 0); if (!data.context) {