conf: Load and stack all config files.

Load and parse config files in the following order:

$PIPEWIRE_CONFIG_DIR or /usr/share/pipewire
/etc/pipewire
$XDG_CONFIG_DIR or ~/.config/pipewire

This ensure we always load a working base config and reduce the
chances of failing because of a back user config file.

The user config file now only needs to contain the section that
needs the be changed.

See #207
This commit is contained in:
Wim Taymans 2021-06-09 12:15:39 +02:00
parent d09df66aec
commit db44fe47ee

View file

@ -64,63 +64,78 @@ static int make_path(char *path, int size, const char *paths[])
return 0; return 0;
} }
static int get_read_path(char *path, size_t size, const char *prefix, const char *name) static int get_read_path(char *path, size_t size, const char *prefix, const char *name,
bool useronly, uint32_t *state)
{ {
const char *dir; const char *dir;
char buffer[4096]; char buffer[4096];
if (prefix[0] == '/') { while (true) {
const char *paths[] = { prefix, name, NULL }; switch ((*state)++) {
if (make_path(path, size, paths) == 0 && case 0:
access(path, R_OK) == 0) if (prefix[0] == '/') {
return 1; const char *paths[] = { prefix, name, NULL };
return -ENOENT; if (make_path(path, size, paths) == 0 &&
} access(path, R_OK) == 0)
if (pw_check_option("no-config", "true")) return 1;
goto no_config; return -ENOENT;
}
dir = getenv("PIPEWIRE_CONFIG_DIR"); break;
if (dir != NULL) { case 1:
const char *paths[] = { dir, prefix, name, NULL }; if (useronly)
if (make_path(path, size, paths) == 0 && break;
access(path, R_OK) == 0) dir = getenv("PIPEWIRE_CONFIG_DIR");
return 1; if (dir != NULL) {
} const char *paths[] = { dir, prefix, name, NULL };
if (make_path(path, size, paths) == 0 &&
dir = getenv("XDG_CONFIG_HOME"); access(path, R_OK) == 0)
if (dir != NULL) { return 1;
const char *paths[] = { dir, "pipewire", prefix, name, NULL }; }
if (make_path(path, size, paths) == 0 && dir = PIPEWIRE_CONFDATADIR;
access(path, R_OK) == 0) if (dir != NULL) {
return 1; const char *paths[] = { dir, prefix, name, NULL };
} if (make_path(path, size, paths) == 0 &&
dir = getenv("HOME"); access(path, R_OK) == 0)
if (dir == NULL) { return 1;
struct passwd pwd, *result = NULL; }
if (getpwuid_r(getuid(), &pwd, buffer, sizeof(buffer), &result) == 0) break;
dir = result ? result->pw_dir : NULL; case 2:
} if (pw_check_option("no-config", "true"))
if (dir != NULL) { return 0;
const char *paths[] = { dir, ".config", "pipewire", prefix, name, NULL }; if (useronly)
if (make_path(path, size, paths) == 0 && break;
access(path, R_OK) == 0) dir = PIPEWIRE_CONFIG_DIR;
return 1; if (dir != NULL) {
} const char *paths[] = { dir, prefix, name, NULL };
if (make_path(path, size, paths) == 0 &&
dir = PIPEWIRE_CONFIG_DIR; access(path, R_OK) == 0)
if (dir != NULL) { return 1;
const char *paths[] = { dir, prefix, name, NULL }; }
if (make_path(path, size, paths) == 0 && break;
access(path, R_OK) == 0) case 3:
return 1; dir = getenv("XDG_CONFIG_HOME");
} if (dir != NULL) {
no_config: const char *paths[] = { dir, "pipewire", prefix, name, NULL };
dir = PIPEWIRE_CONFDATADIR; if (make_path(path, size, paths) == 0 &&
if (dir != NULL) { access(path, R_OK) == 0)
const char *paths[] = { dir, prefix, name, NULL }; return 1;
if (make_path(path, size, paths) == 0 && }
access(path, R_OK) == 0) dir = getenv("HOME");
return 1; 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;
}
break;
default:
return 0;
}
} }
return 0; return 0;
} }
@ -242,53 +257,58 @@ error:
return res; return res;
} }
static int conf_load(const char *prefix, const char *name, struct pw_properties *conf) static int conf_load(const char *prefix, const char *name, bool useronly, struct pw_properties *conf)
{ {
char path[PATH_MAX], *data; char path[PATH_MAX], *data;
struct stat sbuf; struct stat sbuf;
int fd; int fd, count = 0;
uint32_t state = 0;
if (prefix == NULL) { if (prefix == NULL) {
prefix = name; prefix = name;
name = NULL; name = NULL;
} }
if (get_read_path(path, sizeof(path), prefix, name) == 0) { while (true) {
pw_log_debug(NAME" %p: can't load config '%s': %m", conf, path); if (get_read_path(path, sizeof(path), prefix, name, useronly, &state) <= 0)
break;
pw_log_info(NAME" %p: loading config '%s'", conf, path);
if ((fd = open(path, O_CLOEXEC | O_RDONLY)) < 0) {
pw_log_warn(NAME" %p: error opening '%s': %m", conf, path);
continue;
}
data = NULL;
if (fstat(fd, &sbuf) < 0) {
pw_log_warn(NAME" %p: error stat '%s': %m", conf, path);
} else if ((data = mmap(NULL, sbuf.st_size, PROT_READ, MAP_PRIVATE, fd, 0)) == MAP_FAILED) {
pw_log_warn(NAME" %p: error mmap '%s': %m", conf, path);
data = NULL;
}
close(fd);
if (data != NULL) {
count += pw_properties_update_string(conf, data, sbuf.st_size);
munmap(data, sbuf.st_size);
}
}
if (count == 0)
return -ENOENT; return -ENOENT;
}
if ((fd = open(path, O_CLOEXEC | O_RDONLY)) < 0) {
pw_log_warn(NAME" %p: error loading config '%s': %m", conf, path);
return -errno;
}
pw_log_info(NAME" %p: loading config '%s'", conf, 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(conf, data, sbuf.st_size);
munmap(data, sbuf.st_size);
return 0; return 0;
error_close:
close(fd);
return -errno;
} }
SPA_EXPORT SPA_EXPORT
int pw_conf_load_conf(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)
{ {
return conf_load(prefix, name, conf); return conf_load(prefix, name, false, conf);
} }
SPA_EXPORT SPA_EXPORT
int pw_conf_load_state(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)
{ {
return conf_load(prefix, name, conf); return conf_load(prefix, name, true, conf);
} }
/* context.spa-libs = { /* context.spa-libs = {